<?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[Kieran Roberts' Blog | Web Development Articles]]></title><description><![CDATA[I'm Kieran, a Full Stack Developer at Hashnode. I love to help others by simplifying web-dev related topics with some personal experiences thrown in the mix. Check it out!]]></description><link>https://blog.kieranroberts.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 05:10:16 GMT</lastBuildDate><atom:link href="https://blog.kieranroberts.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[cvrsnap.com: Blog post cover image creator to help you publish quicker]]></title><description><![CDATA[Introducing cvrsnap.com 🫰.
CvrSnap is a free tool I recently built as a small side project and it lets you quickly create blog post cover images that you can download as a PNG. It’s designed to help you build something that looks good quickly so you...]]></description><link>https://blog.kieranroberts.dev/cvrsnapcom-blog-post-cover-image-creator</link><guid isPermaLink="true">https://blog.kieranroberts.dev/cvrsnapcom-blog-post-cover-image-creator</guid><category><![CDATA[React]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Fri, 31 Jan 2025 08:04:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738087079970/4616e973-5cd9-43e0-aca1-d077d9cd959c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Introducing <a target="_blank" href="https://cvrsnap.com/">cvrsnap.com</a> 🫰.</p>
<p>CvrSnap is a free tool I recently built as a small side project and it lets you quickly create blog post cover images that you can download as a PNG. It’s designed to help you build something that looks good quickly so you can spend less time designing, and more time publishing. You can customise foreground text and it’s layout, customise the background, choose provided download dimensions, and more. The editor persists your progress if you need take a pause. Behind the scenes CvrSnap is a 1 page client-side React app built using React Router v7, TypeScript, MantineUI, Zustand, IndexedDB, and SST to provision the services Amazon CloudFront, Amazon S3, and Amazon Route 53.</p>
<p>I wanted to share the tool with all of you in case it can be of use to you, and also write a little case study for the project. If you are in need of a nice cover image for your next blog post, please do try it out and let me know if there’s anything else you would want to make your ideal cover image as quickly as possible. You can reach out to me <a target="_blank" href="https://x.com/Kieran6dev">@Kieran6Dev</a>, <a target="_blank" href="https://www.linkedin.com/in/kieran6roberts/">on LinkedIn</a>, or through the comments here.</p>
<h2 id="heading-why-did-i-build-this-tool">Why did I build this tool?</h2>
<p>A blog post cover image is something I need every time I write a new post. I don’t have a current template designed (somewhere like Figma) for me to re-use across my posts and I don’t want to spend the time designing one because I always find it difficult to settle on something when starting from nothing. I just want to visit some domain, click a few buttons and be done with it. You might ask:</p>
<blockquote>
<p>But wouldn’t it take longer to build the app than to design a new cover template in Figma</p>
</blockquote>
<p>Yes. But I am a developer and I thought this would be more fun. Plus it might be useful to someone else.</p>
<p>I don’t care about elaborate blog cover images for my own posts. As long as the title is in the image, and maybe my name as the author. Generic cover images from the internet (Unsplash as an example) are also something I don’t care for. With that in mind I set out to build a simple, clean editing user interface where I could go anytime I needed a new blog cover image.</p>
<h2 id="heading-who-is-the-tool-for">Who is the tool for?</h2>
<ul>
<li><p>Someone who needs a good looking blog cover image with text as the primary foreground, usually for the blog title and author (you can remove the text if you so choose).</p>
</li>
<li><p>You don't want a generic cover image from an internet image platform.</p>
</li>
<li><p>You don't want to spend hours starting from scratch using a design tool. You just want to pick some preset templates, maybe change some font settings etc.</p>
</li>
<li><p>You want to do all of this in a modern and user-friendly editor.</p>
</li>
</ul>
<p>CvrSnap is also <strong>free</strong>.</p>
<h2 id="heading-the-live-site">The live site</h2>
<p><a target="_blank" href="https://cvrsnap.com/">cvrsnap.com</a></p>
<p><a target="_blank" href="https://github.com/kieran6roberts/cvrsnap">codebase</a> - (If you like and use the app, take a second to give it a star on GitHub)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738260283581/8b8f19c5-e1e1-4e5d-8731-0a25c25cd1bd.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-cvrsnap-example-covers">CvrSnap example covers</h2>
<p>Here are some example covers images I quickly built using CvrSnap:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738256980613/4cce1ea1-931f-465b-af77-cf336fde8f1c.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738256774691/d51814e7-27e4-4a91-b904-832106879a7f.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738260053093/e708767c-f382-4ef4-91b7-c67eedbdeecc.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738260073501/ba7bb351-aae0-49e6-afee-f064317a0114.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-requirements">Requirements</h2>
<ol>
<li><p>A very simple landing page</p>
</li>
<li><p>Editing interface</p>
</li>
<li><p>Minimum set of customisation options to start with.</p>
<ol>
<li><p>Text placement, size, font, etc.</p>
</li>
<li><p>Background colors, templates, images etc.</p>
</li>
</ol>
</li>
<li><p>1 click image download in PNG format that accurately resembles the preview.</p>
</li>
<li><p>An editor that would persist the selected design options.</p>
</li>
<li><p>Different download size options.</p>
</li>
<li><p>Light/dark theme toggle.</p>
</li>
</ol>
<h2 id="heading-tech-stack">Tech Stack</h2>
<ul>
<li><p><a target="_blank" href="https://reactrouter.com/home">React Router v7</a></p>
</li>
<li><p><a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a></p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/html-to-image"><code>html-to-image</code></a></p>
</li>
<li><p><a target="_blank" href="https://mantine.dev/">MantineUI</a></p>
</li>
<li><p><a target="_blank" href="https://sst.dev/">SST</a></p>
<ul>
<li><p><a target="_blank" href="https://aws.amazon.com/cloudfront/">Amazon CloudFront</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/s3/">Amazon S3</a></p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/route53/">Amazon Route 53</a></p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-build-specifics">Build Specifics</h2>
<h3 id="heading-react-router-v7-amp-the-rendering-approach">React Router v7 &amp; the rendering approach</h3>
<p>The backbone of the app is built using React Router v7. I started out development in an SSR approach using React Router as a framework. But during development I realised it would be better served as a single page client side application. There would be no sever side data fetching requirements from external API’s or databases. The key for a dashboard type app is to optimise for snappy UI and quick transitions. SSR has been pushed as a ‘default’ for a little while now but I don’t believe it’s necessary in all cases, and overkill (possibly a little detrimental) here.</p>
<p>The new version of React Router was a little confusing to me at first because you can decide to run it as full framework, or as a simple routing library. I think there’s work that can be done to better explain this product now it’s merged with <a target="_blank" href="https://remix.run/">Remix</a>. Currently CvrSnap is running using React Router as a framework with <code>SRR: false</code> in the React Router config to specify we intend to use a single page client rendering strategy. This is primarily due to the fact I started out the app in framework mode without settling on the preferred rendering strategy.</p>
<p>I will likely refactor to use React Router simply as a routing library instead of the framework option currently in use. I believe this is usually how you would adopt a single page with React Router as described <a target="_blank" href="https://reactrouter.com/home#react-router-as-a-library">here</a>. I’m currently putting together a small starter kit (not done) for the the tech behind CvrSnap and I have implemented React Router simply for routing there as a trial run which looks good.</p>
<p>Outside of how the client side rendering is approached here, I am happy with the decision to prefer it to SSR for now.</p>
<h3 id="heading-the-persistent-editor-zustand-indexeddb">The persistent editor: Zustand + IndexedDB</h3>
<p>Before building the app, I knew I wanted to persist the editor state for the user. If the user navigates to a different page, or away from the app, when they come back they should see the same design they left behind.</p>
<p>It’s quite frustrating to use apps in this realm that don’t persist your progress. You make some changes, think you are done and finish up, then realise you actually want to adjust something.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">🤦</div>
<div data-node-type="callout-text">Whoops, now you have to start all over again.</div>
</div>

<p>CvrSnap does not have user authentication, and doesn’t need it. Also no external database storage beyond browser based storage. Since no sensitive data needs to be persisted, browser storage becomes a nice solution for the needs of the app. The data that does need to be persisted is simply non-sensitive arbitrary numbers and strings, corresponding to text content, font sizes etc.</p>
<h4 id="heading-indexeddb">IndexedDB</h4>
<p>The core of the editor state is persisted using IndexedDB. IndexedDB is simply an API for client-side storage of a large amount of structured data. I preferred this option for storing the current cover preview state over <code>Window.localStorage</code> for a few key reasons:</p>
<ol>
<li><p>I would be writing to the storage quite often (as cover settings are updated). Local storage updates are synchronous: blocking the main thread with reads and writes. Therefore performance would likely take a hit.</p>
</li>
<li><p>IndexedDB allows structured data which I would would be working with (objects) without requiring it to be serialised.</p>
</li>
<li><p>IndexedDB can store larger amounts of data, something which might become relevant as the app grows.</p>
</li>
</ol>
<p><code>Window.localStorage</code> is however utilised for a couple of small scale settings like the sidebar open state and sidebar open sections states.</p>
<p>All the current settings of the active cover image preview are saved using IndexedDB in combination with the state management tool Zustand.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">The only editor state that is not currently persisted are user uploaded images. Users can upload their own image to the background. I didn’t want to persist personal images mostly for privacy reasons.</div>
</div>

<p>Zustand is simply a small, fast, barebones state management tool. When you navigate on the client between the <code>/</code> page and <code>/create</code> page, that is Zustand persisting your current editor state. I have integrated Zustand with the <code>persist</code> middleware from <code>zustand/middleware</code>, allowing you to store the Zustand state in a storage option of your choice. You can see this in effect after you perform the initial page load.</p>
<p>To demonstrate the approach, here is a simplified solution:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { create } <span class="hljs-keyword">from</span> <span class="hljs-string">'zustand'</span>;
<span class="hljs-keyword">import</span> { get, set, del } <span class="hljs-keyword">from</span> <span class="hljs-string">'idb-keyval'</span>;
<span class="hljs-keyword">import</span> { persist, createJSONStorage, StateStorage } <span class="hljs-keyword">from</span> <span class="hljs-string">'zustand/middleware'</span>;

<span class="hljs-comment">// Interact with IndexedDB using the promise based keyval store 'idb-keyval'</span>
<span class="hljs-keyword">const</span> indexDBStorage: StateStorage = {
  <span class="hljs-attr">getItem</span>: <span class="hljs-keyword">async</span> (name: string): <span class="hljs-built_in">Promise</span>&lt;string | <span class="hljs-literal">null</span>&gt; =&gt; {
    <span class="hljs-keyword">return</span> (<span class="hljs-keyword">await</span> get(name)) ?? <span class="hljs-literal">null</span>;
  },
  <span class="hljs-attr">setItem</span>: <span class="hljs-keyword">async</span> (name: string, <span class="hljs-attr">value</span>: string): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-keyword">void</span>&gt; =&gt; {
    <span class="hljs-keyword">await</span> set(name, value);
  },
  <span class="hljs-attr">removeItem</span>: <span class="hljs-keyword">async</span> (name: string): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-keyword">void</span>&gt; =&gt; {
    <span class="hljs-keyword">await</span> del(name);
  }
};


<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useStore = create(
  {
   <span class="hljs-attr">hasHydrated</span>: <span class="hljs-literal">false</span>,
   <span class="hljs-attr">setHasHydrated</span>: <span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> set({ <span class="hljs-attr">hasHydrated</span>: state }),
   <span class="hljs-comment">// Rest of the state initialisation + update functions</span>

  },
  {
      <span class="hljs-attr">name</span>: <span class="hljs-string">'editor-storage'</span>,
      <span class="hljs-attr">storage</span>: createJSONStorage(<span class="hljs-function">() =&gt;</span> indexDBStorage),
      <span class="hljs-comment">// Pick the state you want to persist.</span>
      <span class="hljs-attr">partialize</span>: <span class="hljs-function">(<span class="hljs-params">state</span>) =&gt;</span> ({
        <span class="hljs-attr">template</span>: state.template,
        <span class="hljs-attr">primaryText</span>: state.primaryText,
      }),
      <span class="hljs-comment">// Updating version becomes useful when we have breaking changes</span>
      <span class="hljs-attr">version</span>: <span class="hljs-number">1</span>,
      <span class="hljs-attr">onRehydrateStorage</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-function">(<span class="hljs-params">state, error</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (error) {
          <span class="hljs-comment">// do something</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (state) {
          state.setHasHydrated(<span class="hljs-literal">true</span>);
        }
      }
    }
)
</code></pre>
<p>One thing to note about persisting the state in IndexedDB is that the operations are asynchronous. This means the store is hydrated from IndexedDB some time later after store creation (after initial render). Therefore, there is a slight delay in having this data available in the editor. When the storage has been successfully hydrated in the <code>onRehydrateStorage</code> function, we set the <code>hasHydrated</code> state to <code>true</code> and use it wherever it’s needed.</p>
<h3 id="heading-dom-to-image-handling">Dom to image handling</h3>
<p>I experimented with several different packages that allow me to capture a DOM node as an image and saw mixed results. I settled on <a target="_blank" href="https://www.npmjs.com/package/html-to-image">html-to-image</a> for the time being. So far it seems to be the most accurate representation of the DOM I have, with minimal configuration Crucially I was also able to capture <code>clip-path</code> elements correctly using html-to-image, an alternative solution I also was experimenting with (<a target="_blank" href="https://html2canvas.hertzen.com/">html2canvas</a>) did not support this. The background templates make use of the CSS property <code>clip-path</code> to create custom backgrounds so this was a dealbreaker. There’s still some optimisation and experimenting I want to do to optimise the final images.</p>
<p>The logic behind downloading the PNG is pretty simple. The PNG image blob is generated from the DOM with <code>htmlToImage.toBlob</code>, setting the intended dimensions, quality etc. Then the blob is downloaded on the client with the help of <a target="_blank" href="https://www.npmjs.com/package/file-saver">file-saver</a>: <code>fs.saveAs(blob, 'cvrsnap-cover.png')</code></p>
<h3 id="heading-mantineui">MantineUI</h3>
<p>I wasn’t looking to spend time writing custom UI elements and design specs for this app. Since there might be some very interactive UI also, I decided to integrate a UI component library. I read good things about the React component library <a target="_blank" href="https://mantine.dev/">MantineUI</a> and decided it would be a good fit for the project to get things done quicker. It’s open source, TypeScript based, and adaptable to various modern Frameworks. The component list of it’s core library is extensive and I would need to utilise several different various complex element compositions in the app.</p>
<p>CSS modules is recommended alongside Mantine and that’s what I went for, using CSS variables where helpful. From someone who enjoys TailwindCSS as my primary tool, I was missing it a little here but it was also nice to write regular CSS again. Something which I have not done for a while.</p>
<h3 id="heading-infrastructure">Infrastructure</h3>
<p>The last piece of the puzzle is the infrastructure. It’s been a long time since I had to deploy a client side single page application and I was open to exploring different possibilities.</p>
<p>Recently I have been diving into learning and building in the cloud using AWS. <a target="_blank" href="https://aws.amazon.com/cloudfront/">Amazon CloudFront</a>, <a target="_blank" href="https://aws.amazon.com/s3/">Amazon S3</a>, and <a target="_blank" href="https://aws.amazon.com/route53/">Amazon Route 53</a> are some of the services I have been exploring and I set on deploying the site this way. What are these services:</p>
<ul>
<li><p><strong>CloudFront</strong> is a web service that speeds up distribution of your static and dynamic web content.</p>
</li>
<li><p><strong>S3</strong> is an object storage service where you can store data inside containers called buckets.</p>
</li>
<li><p><strong>Route 53</strong> is a highly available and scalable cloud domain name system (DNS) service.</p>
</li>
</ul>
<p>While I had been experimenting and learning so far mostly using the AWS web interface, for CvrSnap I wanted to define the infrastructure as code (IaC). I briefly thought about using the <a target="_blank" href="https://docs.aws.amazon.com/cdk/v2/guide/home.html">AWS Cloud Development Kit (AWS CDK)</a> before coming across the <a target="_blank" href="https://sst.dev/">Serverless Stack (SST</a>) and deciding that would be a nice approach. SST is a framework where you define everything your full-stack app needs in code and SST abstracts away a significant amount of the infra configuration and provisioning. Even more so than the AWS CDK. I though this would help me get to production quicker as I am not yet proficient in provisioning resources using the CDK despite working in and around CDK apps a little, then I could explore the CDK at a later date.</p>
<p>This article on <a target="_blank" href="https://blog.awsfundamentals.com/">AWSFundamentals</a> has a great section detailing IaC, AWS CDK, and the benefits of SST if you want more information: <a target="_blank" href="https://blog.awsfundamentals.com/social-stats-dashboard-sst-nextjs#heading-serverless-stack-sst">blog.awsfundamentals.com/social-stats-dashboard-sst-nextjs#heading-serverless-stack-sst</a></p>
<p>Infra provisioning for the static site is taken care of by SST during deployment using a few simple lines of code:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// infra/web.ts</span>
<span class="hljs-keyword">import</span> { config } <span class="hljs-keyword">from</span> <span class="hljs-string">"../config"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> frontend = <span class="hljs-keyword">new</span> sst.aws.StaticSite(<span class="hljs-string">"Frontend"</span>, {
  <span class="hljs-attr">path</span>: <span class="hljs-string">"packages/frontend"</span>,
  <span class="hljs-attr">build</span>: {
    <span class="hljs-attr">output</span>: <span class="hljs-string">"build/client"</span>,
    <span class="hljs-attr">command</span>: <span class="hljs-string">"pnpm build"</span>,
  },
  <span class="hljs-attr">indexPage</span>: <span class="hljs-string">"index.html"</span>,
  <span class="hljs-attr">domain</span>:
  $app.stage === <span class="hljs-string">'production'</span>
    ? {
        <span class="hljs-attr">name</span>: config.domain,
        <span class="hljs-attr">redirects</span>: [<span class="hljs-string">`www.<span class="hljs-subst">${config.domain}</span>`</span>],
      }
    : <span class="hljs-literal">undefined</span>,
});
</code></pre>
<p>I have also taken the time to dive into what SST does behind the scenes when calling <code>sst.aws.StaticSite</code> during my learning sessions, exploring which resources get created and how they interact. For someone who is still relatively new to building in AWS, I found SST to be really neat. I could get to production much faster with infra guaranteed by SST, and spend time digging into the lower level details afterwards.</p>
<p>I’m excited to explore more possibilities if the app ever requires further resources such as functions, storage etc. If you want to explore SST yourself, this resource provided a nice practical walkthrough: <a target="_blank" href="https://guide.sst.dev/">https://guide.sst.dev/</a></p>
<p>Getting back to the underlying infra, the static build output built is stored in a general purpose S3 bucket. Public access to the bucket is blocked and only the CloudFront distribution has access to the bucket through origin access control settings. I got to really dive into S3 bucket permissions/policies and origin access control settings when I has having a problem with initial deployments. Turns out my build output path was incorrect 😄, but at least I got to dig down into these topics. Finally after configuring Route 53 DNS service for <code>cvrsnap.com</code> , traffic could then be routed to the domain using the CloudFront distribution.</p>
<p>There are multiple guides out there that goes into depth on similar infra step-by-step, search for ‘AWS CloudFront S3 Route 53’ and you’ll find plenty of articles if you’re interested.</p>
<h2 id="heading-what-went-well">What went well</h2>
<h3 id="heading-product-editor">Product: Editor</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738260365894/f9b5edda-fb1b-4a0e-9c74-4af38125e8f3.png" alt class="image--center mx-auto" /></p>
<p>I believe the app provides what I initially intended. Simple templates and options to get you moving quickly. Early on I was experimenting with a more custom approach, allowing dragging and resizing of text and an overall slightly more interactive editor. But I realised that didn’t fit my initial purpose for it not to be complicated and open ended with its options. It has pre-designed layouts and options by design. Data is persisted so the user shouldn’t get that feeling of losing something they started. It also feels fast and snappy which is key.</p>
<p>There is a decent amount of customisation options here, between the text and the backgrounds options and I’m happy that you can make covers that don’t all look completely identical. I’m excited to expand the options here (specifically with more complex templates) while keeping the simplicity of making something look good quickly.</p>
<h3 id="heading-personal-learning">Personal: Learning</h3>
<p>I took a lot of learning and enjoyment from this project. A first time user of React Router v7, MantineUI, and the whole SST setup, I was able to expand my knowledge significantly. I now have the experience to make decisions between stacks involving these tools in the future. It was a great help in letting me become comfortable working with S3 buckets and CloudFront distributions which fits in nicely with the upskilling I’m currently doing.</p>
<p>There is invaluable knowledge and experience you gain when building your own projects from scratch. From the product development side, infrastructure, backend, continuous integration (CI), analytics, testing etc etc.</p>
<h2 id="heading-what-can-be-improved">What can be improved</h2>
<h3 id="heading-mobile-ux">Mobile UX</h3>
<p>The app is mobile responsive, and while I just released a new update to improve the mobile UX, I think there are still improvements to make here. Things like scroll position when switching sections and adding a scroll top button are two things that come to mind. Previously you would have to scroll to the top to see the preview, but I recently shipped and update so you have access to a preview button wherever you are scrolled which helped the UX significantly there.</p>
<p>Finally the default cover image text size is a constant. So on mobile it is too large for the preview size. You can manually reduce it to look appropriate but it’s not ideal. I don’t really want to adjust the font size as the screen size changes but I need to do some thinking on this.</p>
<h3 id="heading-customisation-options">Customisation options</h3>
<p>There can always be more customisation options, as long as they don’t add design fatigue to the user since that would defeat the purpose of the app. I’m not intending to introduce drag/resizing or anything open ended like that. Things like:</p>
<ul>
<li><p>More complex background templates.</p>
</li>
<li><p>Templates that have a placeholder where you can upload an avatar to the cover.</p>
</li>
<li><p>Background gradients.</p>
</li>
</ul>
<h3 id="heading-landing-page">Landing page</h3>
<p>The current landing is a bare bones one without too much thought. The editor was the primary focus but now that’s taking shape, the landing page can get an upgrade.</p>
<ul>
<li><p>Improved hero section</p>
</li>
<li><p>Updated editor image</p>
</li>
<li><p>More sections demonstrating the app and cover image details</p>
</li>
</ul>
<h3 id="heading-react-router-library-or-framework">React router: Library or Framework</h3>
<p>To run the app as a ‘true’ client 1 pager, I will probably refactor the app to use React router only for the routing as explained above. This tech debt could have been avoided if I started out this way.</p>
<h2 id="heading-summary">Summary</h2>
<p>CvrSnap is a free tool designed to help users create custom blog post cover images quickly and easily. Built as a client-side React app, CvrSnap leverages modern technologies like React Router v7, TypeScript, and AWS. The app's editor persistently saves user progress using IndexedDB and Zustand and PNG download is a simple click away.</p>
<p>If you made it this far, you are my hero. If you use CvrSnap to download an image you use in a blog post, please share the post with me on <a target="_blank" href="https://www.linkedin.com/in/kieran6roberts/">LinkedIn</a>, or <a target="_blank" href="https://bsky.app/profile/kieran6dev.bsky.social">Bluesky</a> and I’d be happy to read/share.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738178596969/d5dee314-0e5d-48f8-8a93-11716155fab2.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[5 Simple Tips/Good Practises to Level Up Your React Codebase]]></title><description><![CDATA[I have been working as a Software Engineer in React codebases for almost 4 years now. In that time I have worked closely with many Senior Engineers. Reviewing code, having my own code reviewed, and general collaboration. The way I write code and orga...]]></description><link>https://blog.kieranroberts.dev/5-tips-to-level-up-your-react-codebase</link><guid isPermaLink="true">https://blog.kieranroberts.dev/5-tips-to-level-up-your-react-codebase</guid><category><![CDATA[React]]></category><category><![CDATA[software development]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[best practices]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Thu, 23 Jan 2025 14:06:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737640073586/f63f45fa-3fe6-489a-b92e-88e1fd4babb0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have been working as a Software Engineer in React codebases for almost 4 years now. In that time I have worked closely with many Senior Engineers. Reviewing code, having my own code reviewed, and general collaboration. The way I write code and organise things as a result has evolved.</p>
<p>This article will share some tips and good practises that I employ to help make my code more maintainable, reusable, organised, and performant within a React project. Some of the tips are applicable outside of a React codebase as well. I love to learn so please share any tips you have also in the comments. Let me know if you have a better/different approach to something that’s mentioned.</p>
<p>Here are 5 tips/good practises to consider implementing in your React codebases.</p>
<h2 id="heading-1-use-dumb-components">1. Use ‘dumb’ components</h2>
<p>A dumb component is simply a component who’s purpose is to be presentational (UI). It separates presentation from logic. It might accept some props, and render some UI. Some of the benefits are:</p>
<ul>
<li><p>Better reusability</p>
</li>
<li><p>Better predictability</p>
</li>
<li><p>Better separation of concerns</p>
</li>
<li><p>Simpler to maintain</p>
</li>
</ul>
<p>Any smart logic (state etc.) can be moved to a wrapping container and any data the dumb component requires can be accepted through props.</p>
<h3 id="heading-before-dumb-components">Before ‘dumb’ components</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// ❌ Avoid tying a simple presentational element to complex logic</span>

<span class="hljs-keyword">interface</span> BlogCardProps {
  title: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> BlogCard = <span class="hljs-function">(<span class="hljs-params">{ title }: BlogCardProps</span>) =&gt;</span> {
  <span class="hljs-comment">// Here we are tying the useUser hook to an otherwise simple presentational card</span>
  <span class="hljs-keyword">const</span> { user } = useUser();

  <span class="hljs-keyword">return</span> (
    &lt;article&gt;
      &lt;h2&gt;{title}&lt;/h2&gt;
      &lt;span&gt;{user.name}&lt;/span&gt;
    &lt;/article&gt;
  )
};
</code></pre>
<p>Perhaps we will want to reuse <code>BlogCard</code> later but we might not interested in the user name for that instance. Depending on what <code>useUser</code> is doing, you may be making an API call you don’t need, or running some logic that adds unnecessary performance overhead. Rudimentary example I know but the idea is there.</p>
<h3 id="heading-after-dumb-components">After ‘dumb’ components</h3>
<pre><code class="lang-javascript"><span class="hljs-comment">// ✅ Dumb Component (Better):</span>

interface BlogCardProps {
  <span class="hljs-attr">title</span>: string;
  userName: string;
}

<span class="hljs-keyword">const</span> BlogCard = <span class="hljs-function">(<span class="hljs-params">{ title, userName }: BlogCardProps</span>) =&gt;</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>{blog.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{userName}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span></span>
);

<span class="hljs-comment">// Blog Section becomes the smart container with complex logic</span>
<span class="hljs-keyword">const</span> BlogSection = <span class="hljs-function">(<span class="hljs-params">{ blog }: { blog: Blog }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { user } = useUser();
  <span class="hljs-keyword">const</span> { posts } = useBlogs();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span>&gt;</span>
       {posts.map((post) =&gt; {
         return (
           <span class="hljs-tag">&lt;<span class="hljs-name">BlogCard</span> 
             <span class="hljs-attr">key</span>=<span class="hljs-string">{post.id}</span>
             <span class="hljs-attr">blogTitle</span>=<span class="hljs-string">{blog.title}</span>
             <span class="hljs-attr">userName</span>=<span class="hljs-string">{user?.name</span> ?? '<span class="hljs-attr">Unknown</span>'}
           /&gt;</span>
          )
        })}
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
      ...
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
};
</code></pre>
<p>A better approach is to designate a container component to handle the complex logic, and pass down the data to any presentational components that need it.</p>
<p>Dumb components are some my favourite components and I recommend you keep this in mind the next time you are composing some UI. It makes reusing and testing so much easier.</p>
<h2 id="heading-2-usestate-isnt-always-the-solution">2. <code>useState</code> isn’t always the solution</h2>
<p>The <code>useState</code> hook is often overused. Many times we can perform the desired action with an alternative solution that improves the user experience.</p>
<p>A common case where <code>useState</code> is sometimes seen but not ideal is a component that does sorting/filtering.</p>
<h3 id="heading-before-with-usestate">Before with <code>useState</code></h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// ❌ Non ideal approach with useState:</span>

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

<span class="hljs-keyword">const</span> sortTypes = {
  popular: <span class="hljs-string">'popular'</span>,
  newest: <span class="hljs-string">'newest'</span>
} <span class="hljs-keyword">as</span> <span class="hljs-keyword">const</span>;

<span class="hljs-keyword">type</span> SortType = (<span class="hljs-keyword">typeof</span> sortTypes)[keyof <span class="hljs-keyword">typeof</span> sortTypes];

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> BlogList = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [sortType, setSortType] = useState&lt;SortType&gt;(sortTypes.popular);

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;select value={sortType} onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setSortType(e.target.value <span class="hljs-keyword">as</span> SortType)}&gt;
        &lt;option value={sortTypes.popular}&gt;{sortTypes.popular}&lt;/option&gt;
        &lt;option value={sortTypes.newest}&gt;{sortTypes.newest}&lt;/option&gt;
      &lt;/select&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>The UX of this example is poor for a few different reasons:</p>
<ol>
<li><p>If we refresh, we lose our current sort and the default sort is restored. This can be frustrating to users who want to continue from where they previously were.</p>
</li>
<li><p>We are unable to share a link that includes a specific sort type.</p>
</li>
</ol>
<p>We can implement a much more user friendly approach by utilising the URL as state.</p>
<h3 id="heading-after-using-the-url-as-our-state">After using the URL as our ‘state’</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// ✅ Better approach using the URL:</span>

<span class="hljs-keyword">import</span> { useSearchParams } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router'</span>;

<span class="hljs-keyword">const</span> sortTypes = {
  popular: <span class="hljs-string">'popular'</span>,
  newest: <span class="hljs-string">'newest'</span>
} <span class="hljs-keyword">as</span> <span class="hljs-keyword">const</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> BlogList = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [searchParams, setSearchParams] = useSearchParams();

  <span class="hljs-keyword">const</span> sortParam = searchParams.get(<span class="hljs-string">'sortType'</span>);

  <span class="hljs-keyword">const</span> sortType =
    sortParam &amp;&amp; sortParam <span class="hljs-keyword">in</span> sortTypes ? sortTypes[sortParam <span class="hljs-keyword">as</span> keyof <span class="hljs-keyword">typeof</span> sortTypes] : sortTypes.popular;

  <span class="hljs-keyword">const</span> onSortTypeChange = <span class="hljs-function">(<span class="hljs-params">e: React.ChangeEvent&lt;HTMLSelectElement&gt;</span>) =&gt;</span> {
    setSearchParams(<span class="hljs-function">(<span class="hljs-params">params</span>) =&gt;</span> {
      params.set(<span class="hljs-string">'sortType'</span>, e.target.value);
      <span class="hljs-keyword">return</span> params;
    });
  };

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;select value={sortType} onChange={onSortTypeChange}&gt;
        &lt;option value={sortTypes.popular}&gt;{sortTypes.popular}&lt;/option&gt;
        &lt;option value={sortTypes.newest}&gt;{sortTypes.newest}&lt;/option&gt;
      &lt;/select&gt;
    &lt;/div&gt;
  );
};
</code></pre>
<p>Now our selected state is saved by the url, even after refresh. The user can save/share/bookmark the url and keep the current sort. We fallback to a default sort type <code>popular</code> if the url is not what we expect so the app works as expected. This also now incorporates the browser history allowing users to flick between sorts.</p>
<p>More features that can often make use of the URL as state is</p>
<ul>
<li><p>Filtering/sorting</p>
</li>
<li><p>Pagination</p>
</li>
<li><p>Tab interfaces</p>
</li>
<li><p>Search functionality</p>
</li>
<li><p>Multi stage forms</p>
</li>
</ul>
<p>Keep this in mind the next time your writing your next <code>useState</code> hook.</p>
<h2 id="heading-3-think-about-how-you-organisationstructure-your-code-repository">3. Think about how you organisation/structure your code repository</h2>
<p>This one isn’t strictly React focused, but it applies nonetheless. Repository structure and good organisation is something we all was unsure of at some point. It’s not really something that’s taught akin to learning your chosen coding language. But as you see when you start working in real projects, the structure of the code has real affects on working within it successfully. This is mostly aimed at projects that start to grow beyond the simple few files. Most real React projects will have several components/pages/hooks/utils from early stages.</p>
<p>As your projects grow, you’ll want a structure that is:</p>
<ul>
<li><p>Scalable</p>
</li>
<li><p>Maintainable</p>
</li>
<li><p>Adheres to some good patterns</p>
</li>
</ul>
<p>Ensuring these points are taken care of makes everything easier for you and any collaborators working on the project. Testing, debugging, working in parallel, you name it.</p>
<p>There is no ‘correct’ way of structuring your repository. Projects have different requirements and complexity.</p>
<p>Recently I have been enjoying implementing a ‘Feature-Based Architecture’ for my personal projects. As you may guess, it suggests structuring code by features e.g. <code>editor</code>, <code>preview</code>. I like the scoping here. Each feature directory may have it’s own <code>hooks</code>, <code>utils</code> etc. directories with code only that feature requires. A <code>shared</code> directory is used to include code that is shared across features.</p>
<h3 id="heading-example-of-a-feature-based-hierarchy">Example of a feature-based hierarchy</h3>
<pre><code class="lang-markdown">src/
│
├── features/                   
│   ├── editor/                   # Editor feature
│   │   ├── components/
│   │   │   ├── Drawer.tsx
│   │   │   ├── TextSettings.tsx
│   │   │   └── BackgroundSettings.tsx
│   │   ├── hooks/
│   │   │   └── useEditorData.ts
│   │   ├── styles/
│   │   │   └── Drawer.module.css
│   │   └── index.ts
│   ├── preview/                 # Preview feature
│   │   ├── components/
│   │   │   ├── CoverImage.tsx
│   │   │   ├── CoverImageControls.tsx
│   │   ├── hooks/
│   │   │   └── usePreviewData.ts
│   │   ├── styles/
│   │   │   └── CoverImage.module.css
│   │   │   └── CoverImageControls.module.css
│   │   ├── consts/
│   │   │   └── index.ts
│   │   └── index.ts
│   │
│   └── ... (other features)
│
├── shared/                       # Shared/common code
│   ├── components/               # Reusable components
│   │   ├── Button.tsx
│   │   ├── Modal.tsx
│   ├── hooks/                    # Shared custom hooks
│   │   └── useImageDownload.ts
│   ├── styles/                   # Global or shared styles
│   │   └── global.css
│   ├── layouts/                  # Shared layouts
│   │   └── navbar.tsx
│   ├── utils/                    # General-purpose utilities
│   │   └── logger.ts
├── pages/                        # However you app handles pages etc.
│   ├── index.tsx
</code></pre>
<p>There’s a nice encapsulation about this structure that I find appealing. Despite some of the code being a bit more ‘spread out’ that it would in some structures, I know exactly where everything should be when I create something new and I’m never second guessing or wasting time thinking about it. I hate not knowing where I should place a component/value etc, and this way I have clear pattern.</p>
<p>I have built many projects that started out with a flat/technology based approach. When starting out this way you still need to make decisions about how you are going to organise sub-directories like components. Do you group by location, feature, or something else. You certainly have to group in some way or end up with a directory that’s impossible to navigate nicely. You could group by feature within components for example, but let’s say you want to re-work a feature, or add some upgrades. Now you have to traverse throughout all the parent directories to find what you need like <code>components</code>, <code>hooks</code>, <code>utils</code>, <code>stores</code> and whatever else the feature uses to make your changes. Of course you can make searching the repo easier by keyword searching, but still.</p>
<p>Instead, with the feature based approach I can navigate to my feature and get started. Maybe I have to look in shared but it will be easier to navigate this directory now that most of the logic is encapsulated within the feature directory.</p>
<h3 id="heading-example-of-the-flattechnology-based-hierarchy">Example of the flat/technology based hierarchy</h3>
<pre><code class="lang-markdown">src/
├── components/         
│   ├── Drawer.tsx
│   ├── TextSettings.tsx
│   ├── BackgroundSettings.tsx
│   ├── CoverImage.tsx
│   ├── CoverImageControls.tsx
├── layouts/            # Layout components
│   ├── Header.tsx
├── hooks/              # Custom React hooks
│   ├── useImageDownload.ts
├── styles/             # Global or modular styles
│   └── global.css
├── utils/              # General-purpose utilities and helpers
│   └── logger.ts
├── pages/              # However you app handles pages etc.
│   ├── index.ts/
</code></pre>
<p>Another type of organisational design system you may come across, especially in a React project is ‘Atomic Design’. This usually involves grouping code into categories named something like <code>atoms</code>, <code>molecules</code>, <code>organisms</code>, <code>templates</code> etc. <code>atoms</code> being the smallest elements like a base Button component, and scaling up from there. This structure can work well specifically for React UI components grouping in a project with a lot of components.</p>
<p>And as you may see, it is possible to combine ideas from these repository organisational structures. There are many patterns out there to read about. The idea being that you should read up on the pros/cons of them and implement what you think fits well for your use case.</p>
<h2 id="heading-4-use-common-shared-consts-for-your-non-changing-values">4. Use common shared <code>consts</code> for your non-changing values</h2>
<p>Let’s say we have some content that can be one of several different types. One of these types we identify as <code>posts</code>. It’s a string and will always be <code>posts</code>. You might use it like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">return</span> (
  &lt;Select defaultValue=<span class="hljs-string">"posts"</span> onChange={onItemChange}&gt;
    ...
  &lt;/Select&gt;
  &lt;PostsSection hidden={value !== <span class="hljs-string">"posts"</span>} /&gt;
)
</code></pre>
<p>Here we refer to a string <code>posts</code> more than once. Every time we use the string <code>posts</code> we increase the possibility of misspelling it and causing errors. This is easily avoidable if you use TypeScript, but there is another issue.</p>
<p>Let’s say we get a directive that we now have to change this to the string <code>articles</code>, for whatever reason. Now we have to change every instance where it’s in use. That becomes a major pain if it’s used across multiple files and directories without being referred to by a common <code>const</code>.</p>
<p>Instead we should refer to the string using a <code>const</code> variable like this e.g:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// consts/index.ts</span>

<span class="hljs-comment">// Common practice is use snake_case naming convertion with capitals</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> POSTS_KEY = <span class="hljs-string">"posts"</span>;
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { POSTS_KEY } <span class="hljs-keyword">from</span> <span class="hljs-string">'@consts/index'</span>;

<span class="hljs-keyword">return</span> (
  &lt;Select defaultValue={POSTS_KEY} onChange={onItemChange}&gt;
    ...
  &lt;/Select&gt;
  &lt;SomeOtherComponent hidden={value !== POSTS_KEY}&gt;
)
</code></pre>
<p>Now you have one source of truth. It is common practice to have a directory called <code>consts</code> or <code>constants</code> where you store values that need to be shared across files. If the <code>const</code> is only ever going to be required in one file, it is fine to define it only where it is needed, then move it to a central location later when re-use is needed.</p>
<h2 id="heading-5-passing-primitives-as-props-beats-whole-objects">5. <strong>Passing primitives as props beats whole objects</strong></h2>
<p>Let’s say we have an object <code>blog</code> and this object has several key-value pairs. We have a dumb component that needs access to some of this data. Instead of passing the whole object as one React component prop <code>blog={blog}</code>, it is good to instead prefer passing the individual values required by the dumb component:</p>
<h3 id="heading-what-i-wouldnt-recommend">What I wouldn’t recommend</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// ❌ What I wouldn’t recommend:</span>

<span class="hljs-keyword">type</span> Blog = {
  title: <span class="hljs-built_in">string</span>;
  coverImage: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;
};

<span class="hljs-comment">// BlogCard doesn't even use blog.coverImage</span>
<span class="hljs-keyword">const</span> BlogCard = <span class="hljs-function">(<span class="hljs-params">{ blog }: { blog: Blog }</span>) =&gt;</span> (
  &lt;article&gt;
    &lt;h2&gt;{blog.title}&lt;/h2&gt;
  &lt;/article&gt;
);
</code></pre>
<p>There are a few key reasons this approach is not usually preferred:</p>
<ol>
<li><p>It has access to data it doesn’t need.</p>
</li>
<li><p><code>BlogCard</code> could mutate the original <code>blog</code> object causing side effects. Recently while diving into my AWS learnings, I learned about the <em>‘Principle of least privilege’</em> and it applies here. Essentially we don’t allow the inner component the chance to do more than it should be able to.</p>
</li>
<li><p>Can lead to more unpredictable rendering behaviour due to React’s shallow comparison of props: <a target="_blank" href="https://react.dev/reference/react/memo#troubleshooting">https://react.dev/reference/react/memo#troubleshooting</a></p>
</li>
<li><p>Is less declarative in general.</p>
</li>
<li><p>Might make testing more difficult because there’s more data than required.</p>
</li>
</ol>
<h3 id="heading-better-approach-passing-only-the-required-props">Better approach passing only the required props</h3>
<pre><code class="lang-typescript"><span class="hljs-comment">// ✅ Better Approach:</span>

<span class="hljs-keyword">type</span> Blog = {
  title: <span class="hljs-built_in">string</span>;
  coverImage: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;
};

<span class="hljs-keyword">const</span> BlogCard = <span class="hljs-function">(<span class="hljs-params">{ title }: { title: BlogCard[<span class="hljs-string">'title'</span>] }</span>) =&gt;</span> (
  &lt;article&gt;
    &lt;h2&gt;{blog.title}&lt;/h2&gt;
  &lt;/article&gt;
);
</code></pre>
<p>Now the component is more declarative, we can see exactly what data it accepts. It doesn’t have access to the original blog object and if we wanted to memoize the component, we could skip re-rendering the <code>BlogCard</code> successfully when other fields other than <code>title</code> of the blog object change.</p>
<p>I would prefer this approach almost always even if I needed to pass several different fields. If you see the number of props your component needs is growing, it could be a sign you need to refactor your component into several different different components. Each with less responsibilities than before.</p>
<h2 id="heading-summary">Summary</h2>
<p>This article shares five key tips for improving the maintainability, reusability, organisation, and performance of your React codebases. It covers the benefits of using 'dumb' components as they’re simpler to maintain and re-use, optimising state management beyond <code>useState</code>, and suggests exploring effective code repository structures. It also encourages the use of shared constants for consistency and maintainability, and finally favouring the passing of primitive values over whole objects in component props.</p>
<p>There is a ton of things I didn’t cover in this article that could have been in, I might write another article on the topic. Let me know if you have any interesting tips or best practises that you like to employ in your React codebases. I’d love to hear them.</p>
<p>Until next time!</p>
]]></content:encoded></item><item><title><![CDATA[kieranroberts.dev: My New Dev Portfolio Built With Astro, TailwindCSS, and TypeScript]]></title><description><![CDATA[I wanted to build a new personal developer portfolio for myself. Somewhere to showcase my skills, experience, projects etc. It is now deployed at kieranroberts.dev and I wanted to write up a small case study reflecting on the project. Talk about what...]]></description><link>https://blog.kieranroberts.dev/kieranrobertsdev-my-new-dev-portfolio-built-with-astro-tailwindcss-and-typescript</link><guid isPermaLink="true">https://blog.kieranroberts.dev/kieranrobertsdev-my-new-dev-portfolio-built-with-astro-tailwindcss-and-typescript</guid><category><![CDATA[Astro]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Thu, 16 Jan 2025 12:50:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737027318338/acfb79f0-2716-4dda-94fe-0685082ff2d0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I wanted to build a new personal developer portfolio for myself. Somewhere to showcase my skills, experience, projects etc. It is now deployed at <a target="_blank" href="https://kieranroberts.dev/">kieranroberts.dev</a> and I wanted to write up a small case study reflecting on the project. Talk about what went well, what I might do differently next time, and what can be improved. I will also discuss the tech involved and the thinking behind certain decisions.</p>
<p>I’ve had my eye on <a target="_blank" href="https://astro.build/">Astro</a> for a while, knowing it would be a perfect fit for a simple content-focused site like this. Leveraging TailwindCSS, TypeScript, and Hashnode API’s for the remaining work, here is a look behind the curtain of my new portfolio and how it was put together.</p>
<h2 id="heading-the-final-product">The ‘final’ product</h2>
<p>I’m hesitant to say ‘final’ since it will be iterated on further, but we can say the initial MVP is done:</p>
<p><a target="_blank" href="https://kieranroberts.dev/">kieranroberts.dev</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737026220253/3406a601-a1a5-4562-841f-e6f1497bc9ed.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-problem">The problem</h2>
<p>This a list of notes/requirements I kept in mind before beginning the project:</p>
<ol>
<li><p>I will be a simple content focused site and minimal dynamic elements.</p>
</li>
<li><p>Start out as a simple one-pager.</p>
</li>
<li><p>Fast.</p>
</li>
<li><p>Accessible.</p>
</li>
<li><p>Be search engine optimised.</p>
</li>
<li><p>Clean/simple design style to not get bogged down in design hell.</p>
</li>
<li><p>Light/dark theme.</p>
</li>
<li><p>Free hosting.</p>
</li>
</ol>
<h2 id="heading-site-content">Site content</h2>
<p>The page would have the following sections at a minimum:</p>
<ol>
<li><p>Landing page hero.</p>
</li>
<li><p>Career &amp; experience (relevant stuff only with CV download).</p>
</li>
<li><p>Blog (recent articles only and a link to the blog).</p>
</li>
<li><p>Testimonials</p>
</li>
<li><p>Personal projects.</p>
</li>
<li><p>Additional achievements.</p>
</li>
</ol>
<p>With that mind, it was time to pick the right tools for the job.</p>
<h2 id="heading-tech-stack">Tech stack</h2>
<details><summary>Astro</summary><div data-type="detailsContent">In early 2024 I attended <a target="_self" href="https://kongresjs.pl/">Kongres.js</a> in Warsaw, Poland. There were talks on various tools and frameworks. One of which was Astro. Two of its major pluses is that it is optimised for performance SEO, among other things. Astro uses a server-first approach sending lightweight HTML to the browser, removing unnecessary JavaScript. This made it a perfect choice for this project. And in the future, if I decide to move my blog to <code>kieranroberts.dev/blog</code>, Astro would be a great fit for this use case as well so future-proofing is covered.</div></details><details><summary>TailwindCSS</summary><div data-type="detailsContent">For styling I like TailwindCSS. I love the speed at which I can build styled elements and I am already very comfortable with Tailwind so it would help speed up the building process. Didn’t feel the need to include use of a UI library at this time.</div></details><details><summary>Hashnode GraphQL API’s</summary><div data-type="detailsContent">In order to show a section of my recent blog posts, I would need to fetch the data from the Hashnode GraphQL API’s. I picked graphql-request to handle fetching the data. It’s a simple and lightweight GraphQL client with TypeScript support. I could have skipped it altogether and just used the native <code>fetch</code> but I like opting for this when working with GraphQL. It makes no significant difference to the bundle size here and I anticipate more data fetching in the future.</div></details><details><summary>TypeScript</summary><div data-type="detailsContent">Astro is supplemented with TypeScript to provide type-checking. I would always opt for it over plain JavaScript where possible.</div></details><details><summary>Cloudflare Pages</summary><div data-type="detailsContent">For deployment I chose Cloudflare Pages with auto deployments from Git. As with many hosting providers, it has simple git integration, previews, and a generous free tier.</div></details>

<h2 id="heading-the-approach">The approach</h2>
<p>There weren’t any technical challenges with this project so the primary focus was on the UI/UX and delivering a fast and accessible experience.</p>
<h3 id="heading-how-was-the-astro-experience">How was the Astro experience</h3>
<p>This was my first project building with Astro. Overall it was a pleasant experience. The docs are great and adapting to Astro was straightforward. At first I was experimenting using React alongside Astro which is viable with the help of Astro client directives. But I quickly realised React is overkill here. I wanted to take advantage of astro components <code>.astro</code> as they render to static HTML without client-side runtime, and so I converted any straggling React into Astro files and removed the React dependencies altogether.</p>
<p>An example of this was a light/dark theme toggle. Initially I wrote this in React but it was simply wasn’t needed and therefore I refactored to plain TypeScript:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// ThemeToggle.astro</span>
---
<span class="hljs-keyword">import</span> { Icon } <span class="hljs-keyword">from</span> <span class="hljs-string">'astro-icon/components'</span>;
---

&lt;script&gt;
    <span class="hljs-keyword">import</span> { Themes, THEME_STORAGE_KEY } <span class="hljs-keyword">from</span> <span class="hljs-string">'../consts/index'</span>;
    <span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Theme } <span class="hljs-keyword">from</span> <span class="hljs-string">'../env'</span>;

    <span class="hljs-keyword">const</span> button = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'theme-toggle'</span>);

    <span class="hljs-keyword">const</span> theme = (<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">localStorage</span> !== <span class="hljs-string">'undefined'</span> &amp;&amp; <span class="hljs-built_in">localStorage</span>.getItem(THEME_STORAGE_KEY)) {
            <span class="hljs-keyword">return</span> (<span class="hljs-built_in">localStorage</span>.getItem(THEME_STORAGE_KEY) <span class="hljs-keyword">as</span> Theme) || Themes.light;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">window</span>.matchMedia(<span class="hljs-string">'(prefers-color-scheme: dark)'</span>).matches ? Themes.dark : Themes.light;
    })();

    <span class="hljs-keyword">const</span> reflectThemePreference = <span class="hljs-function">(<span class="hljs-params">theme: Theme</span>) =&gt;</span> {
        <span class="hljs-built_in">document</span>.documentElement.className = <span class="hljs-string">''</span>;
        <span class="hljs-built_in">document</span>.documentElement.classList.add(theme);
        button?.setAttribute(<span class="hljs-string">'aria-label'</span>, <span class="hljs-string">`<span class="hljs-subst">${theme}</span> theme`</span>);
    };

    reflectThemePreference(theme);
    <span class="hljs-built_in">window</span>.localStorage.setItem(THEME_STORAGE_KEY, theme);

    <span class="hljs-keyword">const</span> handleToggleClick = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> element = <span class="hljs-built_in">document</span>.documentElement;
        <span class="hljs-keyword">const</span> newTheme = element.classList.contains(<span class="hljs-string">'dark'</span>) ? Themes.light : Themes.dark;
        reflectThemePreference(newTheme);
        <span class="hljs-built_in">localStorage</span>.setItem(THEME_STORAGE_KEY, newTheme);
    };

    button?.addEventListener(<span class="hljs-string">'click'</span>, handleToggleClick);
&lt;/script&gt;

&lt;button
    id=<span class="hljs-string">"theme-toggle"</span>
    title=<span class="hljs-string">"Toggles light &amp; dark theme"</span>
    <span class="hljs-keyword">class</span>=<span class="hljs-string">"group flex items-center justify-center rounded-full p-1.5"</span>
&gt;
    &lt;span
        <span class="hljs-keyword">class</span>=<span class="hljs-string">"duration-400 block text-zinc-900 transition-transform ease-in-out group-hover:-rotate-90 dark:text-zinc-50"</span>
    &gt;
        &lt;Icon name=<span class="hljs-string">"sun"</span> <span class="hljs-keyword">class</span>=<span class="hljs-string">"block dark:hidden"</span> width=<span class="hljs-string">"24"</span> height=<span class="hljs-string">"24"</span> /&gt;
        &lt;Icon name=<span class="hljs-string">"moon"</span> <span class="hljs-keyword">class</span>=<span class="hljs-string">"hidden dark:block"</span> width=<span class="hljs-string">"24"</span> height=<span class="hljs-string">"24"</span> /&gt;
    &lt;/span&gt;
&lt;/button&gt;
</code></pre>
<p>Removing the React code and dependencies helped reduce the bundle size and ensured I was taking advantage of what Astro offers.</p>
<p>I’m sure as the project grows there will opportunities to explore some of the latest Astro features like Astro Server Islands architecture.</p>
<h3 id="heading-design-overview">Design overview</h3>
<p>Design is usually the tricky part for me. It’s easy to start experimenting too early and lose time to ideas that didn’t work out. This time I was determined to keep things simple at first and then slowly add extra layers over time.</p>
<p>Because I was not attempting complex design from the start, I decided to skip pre-designing the page in Figma. This is something I have done in the past for small side projects and it helped. But on this occasion I wanted to get my ideas into code as soon as possible.</p>
<h3 id="heading-every-site-needs-a-button"><strong>Every site needs a</strong> <code>button</code></h3>
<p>One of the earliest things I did was create a simple <code>button</code> component with a couple variants. To add some polish I utilised CSS transitions and icon slots. The button is one of the first things you notice on a site, and usually what you click first so I wanted to design a nice version.</p>
<h3 id="heading-navbar-ux"><strong>Navbar UX</strong></h3>
<p>Recently I wrote an article on a specific type of UX for a main site navigation and I decided to opt for this behavior on my portfolio. You can find a tutorial on this with more information here:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.kieranroberts.dev/tutorial-implement-a-scroll-translated-dynamic-sticky-navbar-in-react">https://blog.kieranroberts.dev/tutorial-implement-a-scroll-translated-dynamic-sticky-navbar-in-react</a></div>
<p> </p>
<h3 id="heading-color-scheme"><strong>Color scheme</strong></h3>
<p>It’s easy to find a good color scheme. There are countless free tools to help you pick a pre-defined theme. I wanted my primary theme colors to be simple white/dark/gray to keep a clean and modern look. On top of that I added some accent colors to the tailwind config that added a bit more life to the site. They would help bring some ‘pop’ to elements in areas such as focus styling, icons, and background sections.</p>
<pre><code class="lang-javascript">    <span class="hljs-string">'accent-darkest'</span>: <span class="hljs-string">'#6F61C0'</span>,
    <span class="hljs-string">'accent-dark'</span>: <span class="hljs-string">'#A084E8'</span>,
    <span class="hljs-string">'accent-bright'</span>: <span class="hljs-string">'#8BE8E5'</span>,
    <span class="hljs-string">'accent-brightest'</span>: <span class="hljs-string">'#D5FFE4'</span>,
</code></pre>
<h3 id="heading-icons">Icons</h3>
<p>As far as Icons go, I opted to keep a local copy of icons I would use in the repo using <code>/src/icons</code>, saving each icon as a <code>.svg</code>. There is a package called <a target="_blank" href="https://www.astroicon.dev/\)">astro-icon</a> that helps simplify working with icons in Astro projects, this handles embedding the icon.</p>
<p>As far as the icons themselves I opted for icons from the open source project <a target="_blank" href="https://iconoir.com/">iconoir</a>. Largely because of the simple one click copy of the svg code and the large free set of icons available.</p>
<p>If I ever decide to update icons in the future, changing one icon at a time can be easily done.</p>
<hr />
<p>Once those design decisions were made, most of the blockers to writing up core code was done and I could proceed with the build.</p>
<h2 id="heading-outcome">Outcome</h2>
<h3 id="heading-what-went-well">What went well</h3>
<p>Overall I am happy with the outcome. The site feels fast thanks to the lack of JavaScript and server first approach of Astro. It scores well across Lighthouse scores which admittedly should be the minimum standard for a site like this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736951224836/e2cccefb-f3c6-476a-94f6-665cdc11fbb8.png" alt class="image--center mx-auto" /></p>
<p>There was some trial and error with the design but I didn’t get stuck iterating on anything for too long. There’s always room for iteration and improvement. I am not a designer but I was able to harness some inspiration and tips from my experience working with a talented design team. An example is that I paid closer attention to text colors/weights than I would have previously, layering in different shades &amp; weights where appropriate.</p>
<h3 id="heading-what-would-i-do-differently-next-time">What would I do differently next time</h3>
<p>I would have avoided beginning the project with React and Astro, and just started with Astro. I had written some components in React initially but soon realised it wasn’t necessary. Likely that since I had been working with React for so long, it was strange to separate initially.</p>
<h3 id="heading-what-can-be-improved">What can be improved</h3>
<details><summary>Image handling</summary><div data-type="detailsContent">I will consider moving the images used to a suitable CDN as the project and number of images grows. Currently the images are stored locally alongside the code in <code>src/images</code> where Astro optimises and bundles them. There is likely further optimisation that could be done here.</div></details><details><summary>Content</summary><div data-type="detailsContent">I intend to improve upon the content of the site as a main priority. I will be working on some small scale side projects that help make my life easier and that serve as good highlights of my current skills to flesh out the projects section. This section is lacking currently but I’m not interested in filling it out with outdated projects that don’t represent my skills anymore. I will also be writing articles consistently again to add more recent content to the blog.</div></details><details><summary>Automate showing new articles</summary><div data-type="detailsContent">Making sure new articles show up without having to do manual cache purging.</div></details>

<p>There are further minor improvements I intend to make to various sections as well.</p>
<h2 id="heading-summary">Summary</h2>
<p>I recently built a new personal developer portfolio for myself using Astro, TailwindCSS, and TypeScript ensuring a fast, accessible site with great SEO. The project focused on simplicity, speed, and clean design and the case study helped me contextualise the project and seek ways to improve. It was a lot of fun to build and I’m excited about all the things I can add and improve upon in the future.</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Tutorial: Implement a Scroll-Translated, Dynamic Sticky Navbar in React]]></title><description><![CDATA[As a frontend-focused developer, you'll spend a significant portion of your career working on navigation bars. These essential components are integral to almost every modern website and come in various implementations.
In this tutorial we will build ...]]></description><link>https://blog.kieranroberts.dev/tutorial-implement-a-scroll-translated-dynamic-sticky-navbar-in-react</link><guid isPermaLink="true">https://blog.kieranroberts.dev/tutorial-implement-a-scroll-translated-dynamic-sticky-navbar-in-react</guid><category><![CDATA[React]]></category><category><![CDATA[UX]]></category><category><![CDATA[CSS]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Thu, 02 Jan 2025 12:15:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736279545078/b285f98b-1bcc-49ab-a855-de4499dcc60b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a frontend-focused developer, you'll spend a significant portion of your career working on navigation bars. These essential components are integral to almost every modern website and come in various implementations.</p>
<p>In this tutorial we will build a sticky navbar that hides as you scroll down using translation and reappears when you scroll up, based on scroll position calculations. It can provide quite a pleasant navigation experience as it maximizes screen space while keeping important links readily accessible. We will ensure it is accessible, as performant as possible, and leverage React hooks.</p>
<p>Let’s go.</p>
<h2 id="heading-tldr">TLDR</h2>
<p>Here’s how the final version functions:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.loom.com/share/8cc092c9eff1468dafee223d61c698a2">https://www.loom.com/share/8cc092c9eff1468dafee223d61c698a2</a></div>
<p> </p>
<p>Here’s the final code:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// hooks/useStickyHeader.ts</span>

<span class="hljs-keyword">interface</span> UseStickyHeaderProps {
  elRef: React.RefObject&lt;HTMLElement&gt;;
}

<span class="hljs-keyword">const</span> TRANSLATE_BUFFER = <span class="hljs-number">30</span>; <span class="hljs-comment">// in pixels</span>
<span class="hljs-keyword">const</span> QUERY_NAME = <span class="hljs-string">"(prefers-reduced-motion: no-preference)"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useStickyHeader = <span class="hljs-function">(<span class="hljs-params">{ elRef }: UseStickyScrollProps</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> [prefersReducedMotion, setPrefersReducedMotion] = useState(<span class="hljs-literal">true</span>);

  <span class="hljs-keyword">const</span> scrollRef = useRef&lt;{ prevScrollTop: <span class="hljs-built_in">number</span>; animation?: <span class="hljs-built_in">number</span> }&gt;({
    prevScrollTop: <span class="hljs-number">0</span>,
    animation: <span class="hljs-literal">undefined</span>
  });

  <span class="hljs-keyword">const</span> getScrollDistance = <span class="hljs-function">(<span class="hljs-params">{ scrollY }: { scrollY: <span class="hljs-built_in">number</span> }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { prevScrollTop } = scrollRef.current;
    <span class="hljs-keyword">return</span> scrollY - prevScrollTop;
  };

  <span class="hljs-keyword">const</span> getHeaderTopValue = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> headerPosition = elRef.current?.getBoundingClientRect();
    <span class="hljs-keyword">return</span> headerPosition?.top ?? <span class="hljs-number">0</span>;
  };

  <span class="hljs-keyword">const</span> calculateTranslateValue = <span class="hljs-function">(<span class="hljs-params">{
     headerTop,
     scrollDistance,
   }: {
     headerTop: <span class="hljs-built_in">number</span>;
     scrollDistance: <span class="hljs-built_in">number</span>;
   }</span>) =&gt;</span> {
     <span class="hljs-keyword">const</span> navHeight = (elRef.current?.offsetHeight || <span class="hljs-number">0</span>) + TRANSLATE_BUFFER;
     <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.max(
       <span class="hljs-built_in">Math</span>.min(
         headerTop +
           (scrollDistance &lt; <span class="hljs-number">0</span>
             ? <span class="hljs-built_in">Math</span>.abs(scrollDistance)
             : -<span class="hljs-built_in">Math</span>.abs(scrollDistance)),
          <span class="hljs-number">0</span>,
        ),
      -navHeight,
    );
  };

  <span class="hljs-keyword">const</span> onTranslate = <span class="hljs-function">() =&gt;</span> {
    scrollRef.current.animation = requestAnimationFrame(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">const</span> curScrollTop = <span class="hljs-built_in">window</span>.scrollY;
      <span class="hljs-keyword">const</span> scrollDistance = getScrollDistance({ scrollY: curScrollTop });
      <span class="hljs-keyword">const</span> headerTop = getHeaderTopValue();
      <span class="hljs-keyword">const</span> translateAmount = calculateTranslateValue({
        headerTop,
        scrollDistance,
      });

      <span class="hljs-keyword">if</span> (elRef.current) {
        elRef.current.style.transform = <span class="hljs-string">`translateY(<span class="hljs-subst">${amount}</span>px)`</span>;
      }

      scrollRef.current.prevScrollTop = curScrollTop;
    });
  };

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (prefersReducedMotion) {
      <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"scroll"</span>, onTranslate);

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

      <span class="hljs-keyword">if</span> (scrollRef.current.animation)
        cancelAnimationFrame(scrollRef.current.animation);
    };
  }, [prefersReducedMotion]);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> mediaQueryList = <span class="hljs-built_in">window</span>.matchMedia(QUERY_NAME);

    setPrefersReducedMotion(!mediaQueryList.matches);

    <span class="hljs-keyword">const</span> updateMotionSettings = <span class="hljs-function">(<span class="hljs-params">event: MediaQueryListEvent</span>) =&gt;</span> {
      setPrefersReducedMotion(!event.matches);
    };

    mediaQueryList.addEventListener(<span class="hljs-string">"change"</span>, updateMotionSettings);

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      mediaQueryList.removeEventListener(<span class="hljs-string">"change"</span>, updateMotionSettings);
    };
  }, []);
};
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// Navbar.tsx</span>

<span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useStickyHeader } <span class="hljs-keyword">from</span> <span class="hljs-string">'./hooks/useStickyHeader.ts'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> headerRef = React.useRef&lt;HTMLElement&gt;(<span class="hljs-literal">null</span>);
  useStickyHeader({ elRef: headerRef });

  <span class="hljs-keyword">return</span> (
    &lt;header
      ref={headerRef}
      className=<span class="hljs-string">"z-[9999] sticky top-0"</span>
    &gt;
      ...
    &lt;/header&gt;
  )
};
</code></pre>
<p>Here are key features:</p>
<ol>
<li><p>When scrolling down it should gradually disappear above the fold as you would expect with any <code>static</code> positioned element that disappears with viewport scroll.</p>
</li>
<li><p>But as soon as you scroll up the slightest bit, it should start to reveal itself again until it’s fully visible in it’s normal <code>sticky</code> state.</p>
</li>
<li><p>The movement of the navbar will be handled by translating it up and down, we will need to do some calculations based on scroll positions for this.</p>
</li>
</ol>
<h2 id="heading-setting-up-basic-navbar-requirements">Setting Up Basic Navbar Requirements</h2>
<p>The first step is to build your desired navbar. If you already have a working navbar on your site, that will work just fine. There are only a couple of things we must ensure for the UX to match what we have in the demo.</p>
<p>For the purpose of the tutorial, I am using Tailwind for styling and also TypeScript, but neither are required.</p>
<hr />
<p>Our navbar will use <code>sticky</code> positioning. <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/position">MDN</a> defines sticky positioning as the following:</p>
<blockquote>
<p>The element is positioned according to the normal flow of the document, and then offset relative to its <em>nearest scrolling ancestor</em> and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block">containing block (nearest block-</a>level ancestor), including table-related elements, based on the values of <code>top</code>, <code>right</code>, <code>bottom</code>, and <code>left</code>. The offset does not affect the position of any other elements.</p>
</blockquote>
<p>We will vertically position the navbar at the top of the viewport using <code>top: 0px</code>.</p>
<p>Finally we’ll need to track the navbar element for later use when the core logic of the sticky nav is written. We’ll do that by adding a <code>ref</code> to the top level element of the navbar component. A high <code>z-index</code> value has also been added so the navbar is stacked on top of all other content.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> headerRef = React.useRef&lt;HTMLElement&gt;(<span class="hljs-literal">null</span>);

  <span class="hljs-keyword">return</span> (
    &lt;header
      ref={headerRef}
      className=<span class="hljs-string">"z-[9999] sticky top-0"</span>
    &gt;
      ...
    &lt;/header&gt;
  )
};
</code></pre>
<p>Those are essential parts your navbar element should replicate. Next up we need to write the core logic for our desired UX. This will be done by writing a custom React hook. The core logic can be adapted to your requirements.</p>
<h2 id="heading-usestickyheader-react-hook"><code>useStickyHeader</code> React Hook</h2>
<p>Staying in the <code>Navbar</code> component for a second, will use our hook as follows:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useStickyHeader } <span class="hljs-keyword">from</span> <span class="hljs-string">'./hooks/useStickyHeader.ts'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> headerRef = React.useRef&lt;HTMLElement&gt;(<span class="hljs-literal">null</span>);

  useStickyHeader({ elRef: headerRef });

  <span class="hljs-keyword">return</span> (
    &lt;header
      ref={headerRef}
      className=<span class="hljs-string">"z-[9999] sticky top-0"</span>
    &gt;
      ...
    &lt;/header&gt;
  )
};
</code></pre>
<p>Now we need to create the hook. Create a file called <code>useStickyHeader.ts</code> and define the basic outline of the hook:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> UseStickyScrollProps {
  elRef: React.RefObject&lt;HTMLElement&gt;;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useStickyHeader = <span class="hljs-function">(<span class="hljs-params">{ elRef }: UseStickyScrollProps</span>) =&gt;</span> {};
</code></pre>
<h3 id="heading-how-will-the-animation-work">How Will The Animation Work?</h3>
<p>When scrolling down, we are going to translate the navbar upwards to a maximum translation distance of when it’s fully hidden (+ small offset). The navbar is hiding just above the top of the visible viewport so that when you start scrolling up, it immediately starts coming back into view. In that case we translate the navbar down again.</p>
<p>To help illustrate this, in the image below, consider the thick black line to be the top of the visible viewport. Meaning you can’t see anything above that line. This is where the navbar will be positioned when we have scrolled down and hidden the navbar from view. It’s just above the fold and ready to come right back into view.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734866771285/16da719f-24db-4694-a80c-9d422a29cbb7.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-implementing-scroll-based-logic">Implementing Scroll Based Logic</h3>
<p>To do this precisely requires some calculations taking into account the distance that has been scrolled since the last check. Therefore we’ll make use of a <code>scroll</code> event listener. Let’s add this to our hook.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> UseStickyHeaderProps {
  elRef: React.RefObject&lt;HTMLElement&gt;;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useStickyHeader = <span class="hljs-function">(<span class="hljs-params">{ elRef }: UseStickyScrollProps</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> onTranslate = <span class="hljs-function">() =&gt;</span> {};

  useEffect(<span class="hljs-function">() =&gt;</span> {
   <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'scroll'</span>, onTranslate);

   <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
     <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">'scroll'</span>, onTranslate);
   };
 }, []);
};
</code></pre>
<p>The <code>scroll</code> event listener was added inside a <code>useEffect</code> that will run on mount. Make sure to cleanup the event listener when unmounting, that’s what the <code>return</code> statement of a <code>useEffect</code> is for. An empty <code>onTranslate</code> function has also been created for use as the callback for the listener. Logic will be added there shortly.</p>
<p>Next up we need to write out translating code. For that we need to do some calculations. We need to know the number of pixels the document is currently scrolled vertically. This is given by <code>window.scrollY</code>.</p>
<p>Next we calculate how far we have scrolled since the last time the <code>onTranslate</code> function ran. It’s important to note that the <code>scroll</code> listener is not going to fire the <code>onTranslate</code> callback for every <code>1px</code> scrolled. The distance scrolled can be variable.</p>
<p>By tracking the <code>window.scrollY</code> value, we have access to the previous state when calculating the new translation value. A positive value means the user is scrolling down, a negative value means the user is scrolling up.</p>
<p>Let’s put this logic into action. Inside the hook we will write another function <code>getScrollDistance</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Define a ref in the hook to keep track of previous scroll top value</span>
<span class="hljs-keyword">const</span> scrollRef = useRef&lt;{ prevScrollTop: <span class="hljs-built_in">number</span>; }&gt;({
  prevScrollTop: <span class="hljs-number">0</span>,
});

<span class="hljs-comment">// Calculate the distance change from previous check</span>
<span class="hljs-keyword">const</span> getScrollDistance = <span class="hljs-function">(<span class="hljs-params">{ scrollY }: { scrollY: <span class="hljs-built_in">number</span> }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { prevScrollTop } = scrollRef.current;
  <span class="hljs-keyword">return</span> scrollY - prevScrollTop;
};
</code></pre>
<p>and then call this function as part of <code>onTranslate</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> onTranslate = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> curScrollTop = <span class="hljs-built_in">window</span>.scrollY;
  <span class="hljs-keyword">const</span> scrollDistance = getScrollDistance({ scrollY: curScrollTop }); <span class="hljs-comment">// We will use this in a moment</span>

  scrollRef.current.prevScrollTop = curScrollTop; <span class="hljs-comment">// Track our scroll top value</span>
};
</code></pre>
<p>Another required value is the current vertical position of the navbar relative to the viewport. Create a function called <code>getHeaderTopValue</code> for this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> getHeaderTopValue = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> headerPosition = elRef.current?.getBoundingClientRect();
  <span class="hljs-keyword">return</span> headerPosition?.top ?? <span class="hljs-number">0</span>; <span class="hljs-comment">// ?? 0 is just a fallback in case the ref was not found</span>
};
</code></pre>
<h3 id="heading-calculation-pseudocode">Calculation Pseudocode</h3>
<p>With that, all the information is ready to calculate the exact translation value. Before the calculation is written in code, let’s see it in pseudocode to understand what is happening:</p>
<pre><code class="lang-plaintext">Given:
- headerTop: current position of header from top of viewport
- scrollDistance: how far user has scrolled (positive = down, negative = up)
- navHeight: height of navigation + buffer

Step 1: Calculate scroll adjustment

IF scrollDistance is negative (scrolling up)
    adjustment = +|scrollDistance|    // Move header downward
ELSE (scrolling down)
    adjustment = -|scrollDistance|    // Move header upward

Step 2: Calculate new position

newPosition = headerTop + adjustment

Step 3: Clamp the value

maxValue = 0                         // Header can't go above viewport
minValue = -navHeight               // Header can't hide more than its height
finalPosition = CLAMP(newPosition, minValue, maxValue)
</code></pre>
<h3 id="heading-calculate-translate-value">Calculate Translate Value</h3>
<p>Implementing the pseudocode, we end our with our translation value:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> TRANSLATE_BUFFER = <span class="hljs-number">30</span>; <span class="hljs-comment">// in pixels</span>

<span class="hljs-keyword">const</span> calculateTranslateValue = <span class="hljs-function">(<span class="hljs-params">{
    headerTop,
    scrollDistance,
  }: {
    headerTop: <span class="hljs-built_in">number</span>;
    scrollDistance: <span class="hljs-built_in">number</span>;
  }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> navHeight = (elRef.current?.offsetHeight || <span class="hljs-number">0</span>) + TRANSLATE_BUFFER;
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.max(
      <span class="hljs-built_in">Math</span>.min(
        headerTop +
          (scrollDistance &lt; <span class="hljs-number">0</span>
            ? <span class="hljs-built_in">Math</span>.abs(scrollDistance)
            : -<span class="hljs-built_in">Math</span>.abs(scrollDistance)),
        <span class="hljs-number">0</span>,
      ),
      -navHeight,
    );
  };
</code></pre>
<p>The <code>TRANSLATE_BUFFER</code> is not essential but I think it helps with the smoothness of the UX. It adds a small offset so the navbar is an extra 30px above the visible viewport. You can experiment with that value.</p>
<p>Implementing the translation calculation in <code>onTranslate</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> onTranslate = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> curScrollTop = <span class="hljs-built_in">window</span>.scrollY;
  <span class="hljs-keyword">const</span> scrollDistance = getScrollDistance({ scrollY: curScrollTop });
  <span class="hljs-keyword">const</span> headerTop = getHeaderTopValue();
  <span class="hljs-keyword">const</span> translateAmount = calculateTranslateValue({
    headerTop,
    scrollDistance,
  });

  scrollRef.current.prevScrollTop = curScrollTop;
};
</code></pre>
<p>The final step important is to actually perform the css translation with our calculated value. All we have to do is to use the navbar <code>ref</code> we defined earlier and update its <code>style.transform</code> property:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> onTranslate = <span class="hljs-function">() =&gt;</span> {
  requestAnimationFrame(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> curScrollTop = <span class="hljs-built_in">window</span>.scrollY;
    <span class="hljs-keyword">const</span> scrollDistance = getScrollDistance({ scrollY: curScrollTop });
    <span class="hljs-keyword">const</span> headerTop = getHeaderTopValue();
    <span class="hljs-keyword">const</span> translateAmount = calculateTranslateValue({
      headerTop,
      scrollDistance,
    });

    <span class="hljs-keyword">if</span> (elRef.current) {
      <span class="hljs-comment">// Move the navbar up or down in pixels</span>
      elRef.current.style.transform = <span class="hljs-string">`translateY(<span class="hljs-subst">${amount}</span>px)`</span>;
    }

    scrollRef.current.prevScrollTop = curScrollTop;
  });
};
</code></pre>
<p>Notice we wrap the entire function with the <code>window.requestAnimationFrame</code> method which tells the browser you wish to perform an animation. It requests the browser to call a user-supplied callback function before the next repaint.</p>
<p>This performance optimization helps prevents multiple unnecessary calculations within the same frame. Scroll events can fire at a very high rate so we need to optimise the performance. For example, if a user scrolls quickly triggering 100 or more scroll events in a timeframe of say 20ms, <code>requestAnimationFrame</code> will consolidate these into a single frame update. We can ensure our animations remain smooth at the optimal frame rate the device used by the user.</p>
<p>Let’s also expand the scroll <code>ref</code> to eventually allow animation cancelling:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> scrollRef = useRef&lt;{ prevScrollTop: <span class="hljs-built_in">number</span>; animation?: <span class="hljs-built_in">number</span> }&gt;({
  prevScrollTop: <span class="hljs-number">0</span>,
  animation: <span class="hljs-literal">undefined</span>
});
</code></pre>
<p>and then set a reference for our <code>requestAnimationFrame</code> callback using <code>scrollRef.current.animation</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> onTranslate = <span class="hljs-function">() =&gt;</span> {
  scrollRef.current.animation = requestAnimationFrame(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> curScrollTop = <span class="hljs-built_in">window</span>.scrollY;
    <span class="hljs-keyword">const</span> scrollDistance = getScrollDistance({ scrollY: curScrollTop });
    <span class="hljs-keyword">const</span> headerTop = getHeaderTopValue();
    <span class="hljs-keyword">const</span> translateAmount = calculateTranslateValue({
      headerTop,
      scrollDistance,
    });

    <span class="hljs-keyword">if</span> (elRef.current) {
      <span class="hljs-comment">// Move the navbar up or down in pixels</span>
      elRef.current.style.transform = <span class="hljs-string">`translateY(<span class="hljs-subst">${amount}</span>px)`</span>;
    }

    scrollRef.current.prevScrollTop = curScrollTop;
  });
};
</code></pre>
<p>This now allows clean animation cancelling in <code>useEffect</code> cleanup:</p>
<pre><code class="lang-typescript">useEffect(<span class="hljs-function">() =&gt;</span> {
   <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'scroll'</span>, onTranslate);

   <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
     <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">'scroll'</span>, onTranslate);
     <span class="hljs-keyword">if</span> (scrollRef.current.animation) {
       cancelAnimationFrame(scrollRef.current.animation);
     }
   };
 }, []);
</code></pre>
<p>Cancelling the animation frame is necessary because if the component unmounts while an animation frame is pending, you might end up trying to run <code>onTranslate</code> on an unmounted component which will probably lead to errors on memory leaks.</p>
<h3 id="heading-considering-prefers-reduced-motion-css-media-feature">Considering <code>prefers-reduced-motion</code> CSS Media Feature</h3>
<p>An improvement we can make to the hook is to consider users to prefer to reduce animations when visiting sites. <code>prefers-reduced-motion</code> is a CSS media feature in this vein and here is how it described as per <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion">MDN</a>:</p>
<blockquote>
<p>The <code>prefers-reduced-motion</code> <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS">CSS me</a><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media#media_features">dia feature is used to d</a>etect if a user has enabled a setting on their device to minimize the amount of non-essential motion. The setting is used to convey to the browser on the device that the user prefers an interface that removes, reduces, or replaces motion-based animations.</p>
</blockquote>
<p>The transitioning being performed on the navbar may be unwanted by users who prefer to be without animations. We can make use of this media query in the hook and prevent the translate from happening if they have this set on their device. Instead the header will just remain in a regular <code>sticky</code> state.</p>
<p>Here’s the additional logic for checking the user’s preference:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> QUERY_NAME = <span class="hljs-string">"(prefers-reduced-motion: no-preference)"</span>;

<span class="hljs-comment">// Default to true</span>
<span class="hljs-keyword">const</span> [prefersReducedMotion, setPrefersReducedMotion] = useState(<span class="hljs-literal">true</span>);

useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> mediaQueryList = <span class="hljs-built_in">window</span>.matchMedia(QUERY_NAME);

  setPrefersReducedMotion(!mediaQueryList.matches);

  <span class="hljs-keyword">const</span> updateMotionSettings = <span class="hljs-function">(<span class="hljs-params">event: MediaQueryListEvent</span>) =&gt;</span> {
    setPrefersReducedMotion(!event.matches);
  };

  mediaQueryList.addEventListener(<span class="hljs-string">"change"</span>, updateMotionSettings);

  <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
    mediaQueryList.removeEventListener(<span class="hljs-string">"change"</span>, updateMotionSettings);
  };
}, []);
</code></pre>
<p>Here the user’s system setting is checked with <code>window.matchMedia("(prefers-reduced-motion: no-preference)")</code>. Specifically checking if they don’t have a preference with <code>prefers-reduced-motion: no-preference</code> and if it doesn’t match, we know they prefer to have reduced motion, hence we keep <code>setPrefersReducedMotion</code> to <code>true</code>.</p>
<p>Now all we have to do is prevent the scroll listeners from doing their magic in the case where the user wants to prevent animations.</p>
<pre><code class="lang-typescript">useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">if</span> (prefersReducedMotion) {
    <span class="hljs-keyword">return</span>;
  }
  <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'scroll'</span>, onTranslate);

  <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">window</span>.removeEventListener(<span class="hljs-string">'scroll'</span>, onTranslate);

    <span class="hljs-keyword">if</span> (scrollRef.current.animation) {
      cancelAnimationFrame(scrollRef.current.animation);
    }
  };
}, [prefersReducedMotion]);
</code></pre>
<p>Note we also need to add <code>prefersReducedMotion</code> to the dependency array so that changes to the value re-trigger the effect.</p>
<p>And that’s all! Now you should have an accessible, optimized, scroll-positioned navbar with a smooth transition.</p>
<p>You could also extract the media query logic into a separate hook for use elsewhere, or open up our hook for use with other components. Feel free to experiment!</p>
<h2 id="heading-final-implementation">Final Implementation</h2>
<p>Here is the full implementation:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// hooks/useStickyHeader.ts</span>

<span class="hljs-keyword">interface</span> UseStickyHeaderProps {
  elRef: React.RefObject&lt;HTMLElement&gt;;
}

<span class="hljs-keyword">const</span> TRANSLATE_BUFFER = <span class="hljs-number">30</span>; <span class="hljs-comment">// in pixels</span>
<span class="hljs-keyword">const</span> QUERY_NAME = <span class="hljs-string">"(prefers-reduced-motion: no-preference)"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useStickyHeader = <span class="hljs-function">(<span class="hljs-params">{ elRef }: UseStickyScrollProps</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> [prefersReducedMotion, setPrefersReducedMotion] = useState(<span class="hljs-literal">true</span>);

  <span class="hljs-keyword">const</span> scrollRef = useRef&lt;{ prevScrollTop: <span class="hljs-built_in">number</span>; animation?: <span class="hljs-built_in">number</span> }&gt;({
    prevScrollTop: <span class="hljs-number">0</span>,
    animation: <span class="hljs-literal">undefined</span>
  });

  <span class="hljs-keyword">const</span> getScrollDistance = <span class="hljs-function">(<span class="hljs-params">{ scrollY }: { scrollY: <span class="hljs-built_in">number</span> }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { prevScrollTop } = scrollRef.current;
    <span class="hljs-keyword">return</span> scrollY - prevScrollTop;
  };

  <span class="hljs-keyword">const</span> getHeaderTopValue = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> headerPosition = elRef.current?.getBoundingClientRect();
    <span class="hljs-keyword">return</span> headerPosition?.top ?? <span class="hljs-number">0</span>;
  };

  <span class="hljs-keyword">const</span> calculateTranslateValue = <span class="hljs-function">(<span class="hljs-params">{
     headerTop,
     scrollDistance,
   }: {
     headerTop: <span class="hljs-built_in">number</span>;
     scrollDistance: <span class="hljs-built_in">number</span>;
   }</span>) =&gt;</span> {
     <span class="hljs-keyword">const</span> navHeight = (elRef.current?.offsetHeight || <span class="hljs-number">0</span>) + TRANSLATE_BUFFER;
     <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.max(
       <span class="hljs-built_in">Math</span>.min(
         headerTop +
           (scrollDistance &lt; <span class="hljs-number">0</span>
             ? <span class="hljs-built_in">Math</span>.abs(scrollDistance)
             : -<span class="hljs-built_in">Math</span>.abs(scrollDistance)),
          <span class="hljs-number">0</span>,
        ),
      -navHeight,
    );
  };

  <span class="hljs-keyword">const</span> onTranslate = <span class="hljs-function">() =&gt;</span> {
    scrollRef.current.animation = requestAnimationFrame(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">const</span> curScrollTop = <span class="hljs-built_in">window</span>.scrollY;
      <span class="hljs-keyword">const</span> scrollDistance = getScrollDistance({ scrollY: curScrollTop });
      <span class="hljs-keyword">const</span> headerTop = getHeaderTopValue();
      <span class="hljs-keyword">const</span> translateAmount = calculateTranslateValue({
        headerTop,
        scrollDistance,
      });

      <span class="hljs-keyword">if</span> (elRef.current) {
        elRef.current.style.transform = <span class="hljs-string">`translateY(<span class="hljs-subst">${amount}</span>px)`</span>;
      }

      scrollRef.current.prevScrollTop = curScrollTop;
    });
  };

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (prefersReducedMotion) {
      <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">"scroll"</span>, onTranslate);

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

      <span class="hljs-keyword">if</span> (scrollRef.current.animation)
        cancelAnimationFrame(scrollRef.current.animation);
    };
  }, [prefersReducedMotion]);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> mediaQueryList = <span class="hljs-built_in">window</span>.matchMedia(QUERY_NAME);

    setPrefersReducedMotion(!mediaQueryList.matches);

    <span class="hljs-keyword">const</span> updateMotionSettings = <span class="hljs-function">(<span class="hljs-params">event: MediaQueryListEvent</span>) =&gt;</span> {
      setPrefersReducedMotion(!event.matches);
    };

    mediaQueryList.addEventListener(<span class="hljs-string">"change"</span>, updateMotionSettings);

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      mediaQueryList.removeEventListener(<span class="hljs-string">"change"</span>, updateMotionSettings);
    };
  }, []);
};
</code></pre>
<pre><code class="lang-typescript"><span class="hljs-comment">// Navbar.tsx</span>

<span class="hljs-keyword">import</span> { useRef } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useStickyHeader } <span class="hljs-keyword">from</span> <span class="hljs-string">'./hooks/useStickyHeader.ts'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Navbar = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> headerRef = React.useRef&lt;HTMLElement&gt;(<span class="hljs-literal">null</span>);
  useStickyHeader({ elRef: headerRef });

  <span class="hljs-keyword">return</span> (
    &lt;header
      ref={headerRef}
      className=<span class="hljs-string">"z-[9999] sticky top-0"</span>
    &gt;
      ...
    &lt;/header&gt;
  )
};
</code></pre>
<h2 id="heading-summary">Summary</h2>
<p>In conclusion, implementing a scroll-translated, dynamic sticky navbar in React can bring a nice touch to your website. By leveraging React hooks and considering user preferences for reduced motion, developers can create a smooth, responsive, and accessible navigation experience.</p>
<p>Feel free to share the article if it was helpful.</p>
]]></content:encoded></item><item><title><![CDATA[How To Persist Style Changes Through Reloads Using Overrides In Dev Tools]]></title><description><![CDATA[Sidenote
This month on Hashnode we have been running the #DebuggingFeb Writeathon. As a software developer at Hashnode, I wanted to join in. Of course, I won't be eligible for any prizes of course but it's a great opportunity to share knowledge with ...]]></description><link>https://blog.kieranroberts.dev/how-to-persist-style-changes-through-reloads-using-overrides-in-dev-tools</link><guid isPermaLink="true">https://blog.kieranroberts.dev/how-to-persist-style-changes-through-reloads-using-overrides-in-dev-tools</guid><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Sat, 11 Mar 2023 19:21:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736279593192/58215b14-851e-41b9-ba37-52d3f9fef720.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-sidenote">Sidenote</h2>
<p>This month on Hashnode we have been running the <a target="_blank" href="https://hashnode.com/n/debuggingfeb"><strong>#DebuggingFeb</strong></a> Writeathon. As a software developer at Hashnode, I wanted to join in. Of course, I won't be eligible for any prizes of course but it's a great opportunity to share knowledge with the wider developer community here on Hashnode and beyond.</p>
<hr />
<h2 id="heading-introduction">Introduction</h2>
<p>Did you know it is possible to make persistent-through-reload CSS style changes using the in-browser dev tools? This method can also be used for other needs as well as what I'm going to show, so it's a really useful tool to have in your debugging arsenal.</p>
<p>Recently I came across a UI bug that was only visible in our staging environment, and not in my localhost environment. Persisting style changes this way was something I needed to help solve the bug efficiently and cleanly. It's not something you'll likely need very often but is an awesome tool to have in your debugging arsenal.</p>
<h2 id="heading-tldr">TLDR</h2>
<p>Make style changes persist through a reload using the in-browser dev tools.</p>
<ul>
<li><p>Open Chrome dev tools</p>
</li>
<li><p>Visit <strong>Sources tab</strong> -&gt; <strong>Overrides sub-tab</strong> -&gt; <strong>Select folder for overrides -&gt;</strong> Click <strong>Allow -&gt;</strong> Select <strong>Enable Local Overrides -&gt;</strong> Use <strong>Sources/Page tab to select files -&gt;</strong> Right click <strong>Save for Overrides -&gt;</strong> Make your style changes</p>
</li>
<li><p>User the <strong>Sources</strong> tab for style changes if the source of the styles (CSS) is coming from an HTML file.</p>
</li>
<li><p>Profit!</p>
</li>
</ul>
<h2 id="heading-what-bug-was-i-facing">What bug was I facing?</h2>
<p>The bug itself was not so interesting, I'll be honest. But the conditions around the bug made it a more challenging fix to solve efficiently.</p>
<p>We often make use of staging environments as a midpoint between local development and production. This is currently the case for our Hashnode-powered blogs. I'm sure you've noticed some significant changes to your blog recently after we released a multitude of awesome UI and UX enhancements. We have seen a lot of positive feedback for these changes which is awesome to hear.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/hashnode/status/1613868739212066817">https://twitter.com/hashnode/status/1613868739212066817</a></div>
<p> </p>
<p>While the team and I were testing some of these updates in staging, it was reported to me that there was a layout shift during a page load for our new navbar which I was responsible for.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.loom.com/share/7ffc415df11643128117e33b16582134">https://www.loom.com/share/7ffc415df11643128117e33b16582134</a></div>
<p> </p>
<p>If you look closely, you'll notice the size of the top navbar changes once the newsletter and dashboard links come into view. Layout shifts can be super jarring in many situations and should be avoided where possible.</p>
<p>Although the bug itself would likely be adding/removing a style or two, knowing exactly which style needed to be added or removed was problematic without visualisation of any changes. I couldn't see this in my localhost development.</p>
<p>Our staging environment is pretty fast these days, much closer to production than the development environment which helps us catch issues like this before we ship them. I set out to investigate and since I couldn't replicate using localhost, I would have to debug the issue using the staging environment.</p>
<p>The problem was that I didn't want to push changes to the staging branch that I thought <strong><em>might</em></strong> fix the problem, I wanted to know that it was <strong><em>sure</em></strong> to fix the problem. Better to avoid pushing commits and possibly having to revert them, or end up with a cluttered history of commits with descriptions like this:</p>
<blockquote>
<p>fix: pls fix layout shift for main nav 🙃</p>
</blockquote>
<h2 id="heading-solution-using-the-chrome-browser-dev-tools">Solution: Using the Chrome browser dev tools</h2>
<p>Like many of you, I often use the in-browser dev tools as an efficient method of trying out different style changes at speed. It's a great way of making a multitude of quick changes without touching the codebase. If you don't use it often, give it a go.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676717234284/d48902de-c1ca-41a5-8f6f-6bbde6d17fcf.png" alt class="image--center mx-auto" /></p>
<p>Any changes you make to styles this way will disappear when you reload the page. That's a problem when you need those style changes to persist after the reload. I needed to see that the change I would make was fixing the layout shift. That's where the Chrome browser dev tools came to my rescue.</p>
<h3 id="heading-chrome-dev-tools-sources-tab">Chrome Dev Tools 'Sources' Tab</h3>
<p>We can preserve style changes through reload using the <strong>Sources</strong> tab of the Chrome Dev tools. This feature was released a few years back but having just come across this myself, some of you might also be seeing it for the first time.</p>
<p>I'll be using the Chrome dev tools for this tutorial.</p>
<ul>
<li><p>Open up the dev tools through a right click -&gt; <strong>Inspect</strong></p>
</li>
<li><p>Navigate first to <strong>Sources,</strong> then the <strong>Overrides</strong> sub-tab.</p>
<p>  Here we can select a directory on our filesystem that will save style changes we make on whatever domain you need.</p>
</li>
<li><p>Then click on <strong>Select folder for overrides</strong>. Feel free to create a new directory for your changes. You can see all this below.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676717406885/ff8e3f99-a222-42c1-8451-a09d5cdcefd0.png" alt class="image--center mx-auto" /></p>
<ul>
<li>After selecting a file you'll see a warning. Click on <strong>Allow</strong> and don't expose sensitive information as it warns.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676717656528/c02263ea-f519-4a8f-bf7c-453794d8ff43.png" alt class="image--center mx-auto" /></p>
<ul>
<li>After clicking you'll see the directory under the <strong>Overrides</strong> sub-tab. Ensure the checkbox named <strong>Enable Local Overrides</strong> is selected.</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676718139560/28c6a2a1-3e56-4853-99a0-c1947e0e4a1d.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Next up you need to select the sources you'd like to save for overrides. Navigate to the <strong>Page</strong> sub-tab. Right-click on the source where you'd like to make changes and click <strong>Save for overrides.</strong></li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1676717890350/1f8f4cf9-527b-4b0b-a5ae-61acbb9ae33b.png" alt class="image--center mx-auto" /></p>
<p>Now you're finally ready to make some styling changes.</p>
<p>The way you make these changes has some conditions If you make style changes in the Elements tab using the DOM (Document Object Model) tree, changes will not be saved and you should use the Sources tab instead. Find your file there and make edits.</p>
<p>Secondly, when editing CSS in the Styles pane, if the source of the CSS is an HTML file then any changes made won't be saved by DevTools. It's better to edit the HTML file in the Sources panel to ensure any changes are saved.</p>
<p>Here's a quick video of style changes persisting through reload:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.loom.com/share/3c95b54487544d6e8d32a466ce7cbc95">https://www.loom.com/share/3c95b54487544d6e8d32a466ce7cbc95</a></div>
<p> </p>
<p>When an override is in effect you'll notice a little purple circle on top of the file icon and filename.</p>
<h3 id="heading-what-else-can-you-use-this-for">What else can you use this for?</h3>
<p>We can use local overrides for things other than style changes as well. Some of the use cases that come to mind are:</p>
<ul>
<li><p>Testing potential performance improvements</p>
<ul>
<li><p>Using different fonts</p>
</li>
<li><p>Changing script load orders</p>
</li>
</ul>
</li>
<li><p>Trying out changes or debugging potential issues in external libraries</p>
</li>
</ul>
<p>It's likely not something you'll need to use very often but will come in very handy in some of these specific use cases.</p>
<p><strong>Note:</strong> It's a good idea to remove your overrides after you are done.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Using this method I was able to run through some minor style changes very quickly and find/fix the culprit. It ended up being a highly time-efficient solution to a potentially frustrating problem.</p>
<p>It allowed me to cut the effort in debugging -&gt; shipping by a considerable amount. I hope you find it useful in the future too.</p>
<p>See you next time 👋</p>
]]></content:encoded></item><item><title><![CDATA[My First Year As a Software Developer Reviewed In 30 Questions!]]></title><description><![CDATA[My first year as a Software Developer at Hashnode recently passed and I want to share it with you. I thought it would a good idea to format this into 30 simple questions and try to offer tips and suggestions for anyone reading that might benefit from...]]></description><link>https://blog.kieranroberts.dev/my-first-year-as-a-software-developer-reviewed-in-30-questions</link><guid isPermaLink="true">https://blog.kieranroberts.dev/my-first-year-as-a-software-developer-reviewed-in-30-questions</guid><category><![CDATA[software development]]></category><category><![CDATA[Developer]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Wed, 06 Jul 2022 07:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736279628113/61f57ef6-940c-4a01-b71d-ad409593a0da.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>My first year as a Software Developer at Hashnode recently passed and I want to share it with you. I thought it would a good idea to format this into 30 simple questions and try to offer tips and suggestions for anyone reading that might benefit from what I learned.</p>
<p>I am a self taught developer who began learning to code in March 2020, before landing a Full Stack Developer role here at Hashnode in May the following year.</p>
<p>This is my first year as a Software Developer reviewed in 30 questions.</p>
<h2 id="heading-overall-do-you-think-it-was-successful">Overall, do you think it was successful?</h2>
<blockquote>
<p><strong>Yes, absolutely.</strong></p>
</blockquote>
<p>I had my ups and downs, without doubt. There were moments I doubted myself, even moments I thought I was letting others down. </p>
<p>Despite this, I know that I was able to deliver in the end. I feel like I contributed some really cool additions to the product. Specifically on the features side which we pushed hard the last year.</p>
<p>Now I know that I belong in my role, and for my first year, that was always going to be enough for me to be happy that it was successful. My job now is kick on and push things further.</p>
<h2 id="heading-how-did-you-end-up-in-your-role-at-hashnode">How did you end up in your role at Hashnode?</h2>
<p>Sandeep (Co-Founder of Hashnode) reached out to me on Twitter and offered an interview for the role of Full Stack Developer as he suggested I could be a fit for the team. Fortunately for me, those interviews went pretty well and I was hired soon after.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/Kieran6dev/status/1417866909765222401">https://twitter.com/Kieran6dev/status/1417866909765222401</a></div>
<ul>
<li>I didn't end up applying for any roles before landing the job.</li>
<li>I didn't need to build a portfolio, my Hashnode blog was more effective and time-efficient than any portfolio I could have ever spent time building.</li>
<li>Being a regular user of the product was essential.</li>
<li>Connecting with tech related people through Twitter is a great way of finding opportunities. Help recruiters find you first. This is especially helpful when trying to break into the field.</li>
<li>There were no algorithm tests in the hiring process.</li>
<li>I had a take home challenge to complete over the weekend which was very much related to things you might end up doing on the job.</li>
<li><strong>You can do it too!</strong> - <a target="_blank" href="https://careers.hashnode.com/jobs">Hashnode Careers Page</a></li>
</ul>
<h2 id="heading-what-features-and-other-things-did-you-work-on">What features and other things did you work on?</h2>
<ul>
<li><a target="_blank" href="https://townhall.hashnode.com/table-of-contents-feature">Table of Contents</a></li>
<li><a target="_blank" href="https://townhall.hashnode.com/introducing-article-scheduling-feature-for-all-hashnode-blogs">Article scheduling</a></li>
<li><a target="_blank" href="https://townhall.hashnode.com/unsplash-integration">Unsplash integration for cover images</a></li>
<li>Recover deleted articles</li>
<li><a target="_blank" href="https://townhall.hashnode.com/hashnodes-annual-tech-awards-2021">2021 End-of-year awards and review</a></li>
<li>Several team publication updates</li>
<li>Editor updates and bug fixes</li>
<li>End-to-end testing setup and CI integration </li>
<li><a target="_blank" href="https://townhall.hashnode.com/powerful-and-superfast-hashnode-blogs-now-powered-by-nextjs-11-and-vercel">Blogs migration contribution</a></li>
</ul>
<p>As well as several more bug fixes, tech debt, and documentation work.</p>
<h2 id="heading-which-feature-are-you-most-proud-of">Which feature are you most proud of?</h2>
<blockquote>
<p><strong>I think it would be the Table of Contents feature.</strong></p>
</blockquote>
<p>It was a lot of fun to build, offered an interesting challenge in trying to generate the headings in the correct structure, and was one of my earliest significant contributions. You never forget your first.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656073720710/QNHX7O9iv.png" alt="Screenshot 2022-06-24 at 13.28.16.png" /></p>
<p>We still see some really great feedback for this feature as well. Our users are responsible for driving many of the features we build. Without that I would be building for myself and there's not as much fun to be had in that case.</p>
<h2 id="heading-what-kind-of-struggles-did-you-experience">What kind of struggles did you experience?</h2>
<p>Where do I start.</p>
<p>I struggled a little with communication at times. It's easy to feel as though you don't want to bother someone with your 'stupid' questions despite people stressing otherwise. Or maybe you feel like you can figure it out yourself and before you know it, 3 hours have passed and you're still in the same spot. It can often be the case that 5 lines of code take 50,000 lines of thought and it's easy to get lost in it all.</p>
<p>You soon learn that over-communicating is key and I was encouraged to do so. We're fortunate to have a culture of open communication at Hashnode where sharing discussions, problems, and any general questions are encouraged and everyone is extremely helpful.</p>
<p>I would sometimes also make simple mistakes when trying to deliver within a tight timeframe. I learned the hard way that the smallest of changes can have effects that can't be anticipated. Any change has to be verified thoroughly, even if you think it can't possibly affect things.</p>
<p>Surely I'm not the only one who has shipped 'fixes' only to later find out that it ended up breaking something else, or not working exactly as I had thought it was. That has probably been the most demoralising thing I've done.</p>
<p>But we all make mistakes, it's our job to learn from then so we can make less mistakes moving forward.</p>
<h2 id="heading-do-you-enjoy-working-from-home">Do you enjoy working from home?</h2>
<blockquote>
<p><strong>I love it, most of the time.</strong></p>
</blockquote>
<p>It's my first experience working from home outside of school or university and I'm not sure I ever want to go back. It's the little things for me.</p>
<ul>
<li>Jumping in the shower on a lunch break.</li>
<li>Cooking lunch fresh.</li>
<li>Always being home for deliveries.</li>
<li>Not wasting time travelling to and from work.</li>
</ul>
<p>There is a part of me that sometimes misses the jokes and general banter you can have in person, but the benefits definitely outweigh the alternative, for me at least.</p>
<h2 id="heading-can-you-share-one-tip-youve-learned-while-working-remotely">Can you share one tip you've learned while working remotely?</h2>
<blockquote>
<p><strong>Work to a schedule that works for you within the boundaries you're given, and don't feel bad about having a cutoff point.</strong></p>
</blockquote>
<p>Just because you work from home, does not mean you're always on call (<em>unless you are actually on-call of course</em>). It's easier to separate yourself from work when your in-office but it's more difficult to switch off while working from home.</p>
<p>It's ok to close your laptop at the end of the day and get back to something on your schedule. Try leaving the office at the end of the day to create that separation. I like spending the end of the day outside if the weather permits.</p>
<p>Keep your mind fresh and excited for the times when you should be working. At least for me, spending time on my other interests outside of work hours helps keep me fresh and motivated for work. I know the feeling of burnout and trust me, you don't want it.</p>
<h2 id="heading-how-was-the-onboarding-process">How was the onboarding process?</h2>
<blockquote>
<p><strong>Extremely fun, but difficult at times.</strong></p>
</blockquote>
<p>When I joined, the team was a lot smaller than it is now. Up to that point, I think there hadn't been the need to have a fleshed out onboarding process. It was a smaller early stage startup after all, so of course that's understandable. </p>
<p>There were also few processes in place at the time for things like work tracking, code review, and so on. Documentation was lacking too but despite this, everyone on the team was extremely helpful and were also open to any questions or struggles I was facing.</p>
<p>We have now seen many new members join in various roles over the last 9 months and so the team have made a big effort to get the proper processes and documentation in place. Onboarding now would have been a smoother process for sure.</p>
<h2 id="heading-whats-one-superpower-youve-noticed-from-your-co-workers">What's one superpower you've noticed from your co-workers?</h2>
<blockquote>
<p><strong>Knowing the right questions to ask.</strong></p>
</blockquote>
<p>It sounds strange to say that I often didn't feel like I knew what questions I should have been asking. Experienced developers are really good at asking the right questions. If you know, you know.</p>
<h2 id="heading-do-i-think-its-important-to-spend-non-work-related-time-with-your-fellow-remote-workers">Do I think it's important to spend non-work related time with your fellow remote workers?</h2>
<blockquote>
<p><strong>Absolutely</strong></p>
</blockquote>
<p>I love joining our team fun calls where we usually end up chatting about all sorts of stuff. Sometimes over pizza, and occasionally we will end up in a game of Scribble. </p>
<p>Vamsi is the resident Hashnode Scribble champ. I'm a very competitive person so that pains me to admit. I did win once though and I'll cling onto that forever. Never forget.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656955167113/8a2ydKhKQ.png" alt="Discord team chat after I won the game of scribble. Me gloating and the team congratulating. " /></p>
<p>We can often feel isolated as remote workers and sometimes it does the world of good to talk face-to-face with your team about non-work related stuff. Especially great at the end of a tough day.</p>
<h2 id="heading-what-was-your-scariest-moment">What was your scariest moment?</h2>
<p>I could say my first day on the job, but that's obvious so I'll choose something else.</p>
<blockquote>
<p><strong>Personally deploying to production for the first time.</strong> </p>
</blockquote>
<p>Earlier on my code would often be deployed to production by the reviewer, but we have since moved on to a different review and deployment process that helps us move faster as a team.</p>
<p>My first deployment didn't have any user facing changes. I was just merging some test related code but it didn't stop me worrying I would somehow break something. It didn't help that Sentry suffered some downtime while I was merging which caused the initial preview build to fail. </p>
<p>This gave me a small panic attack, but all went well in the end.</p>
<h2 id="heading-what-tool-or-service-do-you-enjoy-using-the-most">What tool or service do you enjoy using the most?</h2>
<blockquote>
<p><strong>Notion</strong></p>
</blockquote>
<p>Not because there's anything specific I like about Notion, but because I enjoy the act of writing documentation. We currently use Notion for our in-house documentation. It's a nice change of pace from writing code and combines technical skills with writing and teaching which I think suits my strengths.</p>
<h2 id="heading-what-tool-or-service-do-you-enjoy-using-the-least">What tool or service do you enjoy using the least?</h2>
<blockquote>
<p><strong>Jira</strong></p>
</blockquote>
<p>I appreciate it's a much needed tool for software teams and I don't dislike using it anymore, but I had to choose something.</p>
<h2 id="heading-what-was-the-most-memorable-bug-fixing-experience">What was the most memorable bug fixing experience?</h2>
<blockquote>
<p><strong>The editor scroll jump bug, definitely.</strong></p>
</blockquote>
<p>There was a bug that caused the editor to do an instant scroll when hitting the enter key, scrolling to a position where the cursor would now be at the top of the textbox. It was as random an act as is seemingly possible in our field. Sometimes it would jump and sometimes not, with little consistency to be able to find a pattern.</p>
<p>Eventually I noticed similar behaviour on other sites. I had an 'ahaa!' moment when I was opening a GitHub pull request and noticed a similar event.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.loom.com/share/23ecd7d40e584001989fd90194217d29">https://www.loom.com/share/23ecd7d40e584001989fd90194217d29</a></div>
<p>It was browser specific issue that affected textboxes and required a workaround fix in our current situation. I'll never forget the pain this bug caused me trying to understand why it was happening. </p>
<p>I'm thinking of writing an article about this in the future so I won't give it all away just yet.</p>
<h2 id="heading-how-can-you-contribute-more-to-hashnode-in-the-future">How can you contribute more to Hashnode in the future?</h2>
<blockquote>
<p><strong>Get back into writing and social sharing.</strong></p>
<p><strong>Decide on my next learning path.</strong></p>
</blockquote>
<p>This is a question I have been asking myself recently. Now I feel settled in my role, I want to take the next steps forward. For me, and for the product.</p>
<p>Writing took a backseat this year. I focused on trying to become a valued member of the development team. Code and processes were my first priority. Recently I published my first article to the Hashnode Engineering blog and I'm excited to contribute more often.</p>
<p>I also want to focus on some learning path. I'm interested in cloud computing and have started exploring this more with AWS recently. Here at Hashnode, they are extremely helpful with supporting team members in advancing their skills and I look forward to taking advantage of this in the next year so I can put what I learn into practice.</p>
<h2 id="heading-what-was-your-worst-period-of-the-year-and-why">What was your worst period of the year and why?</h2>
<blockquote>
<p>Oct 2021 - Jan 2022</p>
</blockquote>
<p>This three month period at the end of 2021 saw me moving from the UK to Ireland. It was a stressful period where my girlfriend and I were moving around temporary accommodation often while looking for something permanent. It was a difficult time to be in need of accommodation with high competition and low availability, all while continuing to work.</p>
<p>This definitely put a strain on me and my mood and I think it affected my work too. Thankfully, all of this is now behind me.</p>
<h2 id="heading-what-is-it-like-working-in-a-small-team-startup">What is it like working in a small team startup?</h2>
<ul>
<li>Extremely exciting.</li>
<li>Always changing and progressing forward.</li>
<li>Very rewarding with large variation of work.</li>
<li>Fast moving.</li>
</ul>
<h2 id="heading-did-you-experience-imposter-syndrome">Did you experience Imposter Syndrome?</h2>
<blockquote>
<p><strong>Of course, I think we all do.</strong> </p>
</blockquote>
<p>There were days I didn't think I would get to the end of. My biggest bug-bear is feeling like a burden on others. I tried my best to alleviate that problem the most because I knew it had have the biggest affect on my mind.</p>
<p>Early on I would work the odd Saturday or Sunday, not because someone asked me but out of my own determination to keep up. I didn't want to fall behind on something that might affect others. It was my way of coping because I definitely wasn't going to let myself sink.</p>
<p>Another thing that helped me was the code review process. The more code review I took part in on both review and reviewee sides, the more I realised that I belong where I am. Taking part in reviews with different people helps you see many different perspectives and learn from others. It also allows you to share what you know and it was a great confidence boost when you can teach something.</p>
<h2 id="heading-what-does-your-daily-work-routine-look-like">What does your daily work routine look like?</h2>
<p>It looks something like this 👇.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1656074045997/G8pk8s0zW.png" alt="Screenshot 2022-06-24 at 13.33.06.png" /></p>
<p>Around 30 mins before the first focus time is my chance to catch up on messages, emails, threads, and check if I have code reviews to do for the day. Lunch break usually takes place in-between focus times and some more casual tasks are reserved for the end of the day, after my final focus time.</p>
<h2 id="heading-how-did-the-job-affect-your-need-to-code-outside-of-work">How did the job affect your need to code outside of work?</h2>
<p>I'd be lying if I said my desire to code outside of work hours wasn't affected. I was so focused on getting up to speed as a team member that I often didn't have the mental energy to want to write code in my spare time. </p>
<p>It's a normal thing to want to step away from the things you do everyday at work when you're off the clock, despite what Twitter might have you believe.</p>
<p>Slowly this has been coming back to me though. I have some project ideas in the brain backlog but I'm more inclined to focus on learning instead of building at the moment.</p>
<h2 id="heading-what-do-you-think-is-the-most-important-skill-to-have-in-a-remote-async-team">What do you think is the most important skill to have in a remote async team?</h2>
<blockquote>
<p><strong>Communication</strong></p>
</blockquote>
<p>As I have come to learn, over-communicating is a necessity for async teams. I have been guilty of forgetting this at times in the last year.</p>
<p>It's super easy to get into a rut of single-minded focus on something, but a little update will go a long way. If something is taking longer than expected, that's fine. Communicate this and keep the person in the loop. You might even get some help as a result of the update/discussion.</p>
<h2 id="heading-based-on-your-experience-share-one-thing-that-helped-you-produce-better-code">Based on your experience, share one thing that helped you produce better code?</h2>
<blockquote>
<p><strong>Embrace code reviews.</strong></p>
</blockquote>
<p>Code reviews are an essential part of team development. Having regular constructive code reviews from different people is extremely helpful. We each bring something different to reviews and we can all learn something from each other.</p>
<p>Ask questions, be respectful, don't be overly nitpicky, and you'll be making forward strides in no time.</p>
<h2 id="heading-how-much-time-do-you-spend-in-meetings">How much time do you spend in meetings?</h2>
<blockquote>
<p><strong>Google calendar is telling me I have an average of 1.7 hours per week.</strong> </p>
</blockquote>
<p>The benefits of an async work culture mean you spend less time in meetings, and end up enjoying every minute of those meetings far more because it's a rarer occurrence.</p>
<h2 id="heading-what-do-you-see-yourself-moving-towards">What do you see yourself moving towards?</h2>
<blockquote>
<p><strong>I'm looking to get more experience with the cloud and serverless computing.</strong> </p>
</blockquote>
<p>This approach is something we often employ at Hashnode for various purposes and I would like to get exposure on this front moving forward. I'm now testing the waters with AWS starting with the free resources related to the Cloud Practitioner certification. Looking forward to getting hands-on here in the future.</p>
<h2 id="heading-do-you-think-a-fully-remote-role-is-suitable-for-an-early-career-developer">Do you think a fully remote role is suitable for an early career developer?</h2>
<blockquote>
<p><strong>Yes, with a few caveats.</strong></p>
</blockquote>
<p>I was a little unsure how it would turn out having a fully remote role in my first job. I worried that I might not get the direction I needed, or that getting help would be difficult because I knew I'd need it.</p>
<p>I think the following are important if you're going to make the most of it:</p>
<ul>
<li>You should be a responsible person and capable of managing your time and effort without a direct push from someone.</li>
<li>Get to know your teammates quickly, this will help you feel comfortable asking for help.</li>
</ul>
<p>A good idea is to have quick 1 on 1 calls with your team members (or at least those you'll be working closest with if you're working in a larger team/company) to get to know everyone individually. I didn't do this personally because I didn't know it was a common practice back then, but I would now if I was starting out again.</p>
<h2 id="heading-can-you-share-some-work-related-termsphrases-you-didnt-know-before-you-started-your-role">Can you share some work related terms/phrases you didn't know before you started your role?</h2>
<p>Many of these are related to working in a team environment which like me, you likely won't know much about before you start working in industry.</p>
<ul>
<li>Sprints</li>
<li>Scrum framework &amp; Agile methodology</li>
<li>Burndown &amp; velocity charts</li>
<li>Stories, Epics, Bugs &amp; Initiatives</li>
<li>Standup</li>
</ul>
<p>If any of these are unfamiliar to you, look them up and get a basic understanding of each. Having knowledge of this is useful and some of it might even come in handy in an interview situation.</p>
<h2 id="heading-what-is-one-selfish-thing-i-want-to-see-from-hashnode">What is one selfish thing I want to see from Hashnode?</h2>
<blockquote>
<p><strong>Hashnode hat swag 🧢🙏</strong></p>
</blockquote>
<h2 id="heading-based-on-your-experience-can-you-share-one-piece-of-advice-to-help-other-developers-find-their-first-role">Based on your experience, can you share one piece of advice to help other developers find their first role?</h2>
<blockquote>
<p><strong>Start writing.</strong></p>
</blockquote>
<p>In my opinion (<em>and many others</em>), you make yourself a far more attractive prospect to potential employers if you maintain a consistently good blog. You can get started with this very easily here on Hashnode. If you didn't already know, this blog is a Hashnode blog. You can setup your very own blog/portfolio in minutes <a target="_blank" href="https://hashnode.com/">here</a>.</p>
<p>Self built portfolio sites usually aren't that interesting and are often very similar from one to the next. We've all seen them a hundred times. I'll admit, they can be fun to build. It gives you your own corner of the internet where you can do what you want. But it's not really a learning exercise in most cases. I would say the time it would take to build your own portfolio can be better spent.</p>
<p>Consider writing about your personal projects in blog form as an alternative to just including links to the live site. Treat each project as a case study and share:</p>
<ul>
<li>Why you used certain tech over alternatives.</li>
<li>What you struggled with.</li>
<li>What you would do differently next time.</li>
</ul>
<p>Take this personal project I built as an example of how you can share your projects in a little more detail:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.kieranroberts.dev/i-built-a-full-stack-serverless-e-commerce-site-with-nextjs-heres-an-honest-review-of-what-i-learned-and-how-it-might-help-you">https://blog.kieranroberts.dev/i-built-a-full-stack-serverless-e-commerce-site-with-nextjs-heres-an-honest-review-of-what-i-learned-and-how-it-might-help-you</a></div>
<p>Also write about related topics that interest you and show everyone you know what you're talking about. If you're self-taught like I am, It's your responsibility to prove you can do the job because you won't have the certificate to prove otherwise.</p>
<h2 id="heading-ok-so-where-did-your-articles-go-this-year">Ok, so where did your articles go this year?</h2>
<p>Feels rich of me to suggest writing to you when I've lost my way a bit on that front. I found it difficult to motivate myself to keep writing if I'm honest. My aim was to put my all my focus at getting up to speed as an engineer with code, processes and everything else as I previously mentioned.</p>
<p>Instead of writing, I'd spend time reading about something that would directly help me with work related stuff. I needed to do all this for myself and I've accepted that now. </p>
<p>It didn't help that I was also going through a stressful move as well. Now that I'm back in a good place, I would love to continue writing again. This is a good place to start.</p>
<h2 id="heading-finally-if-you-could-give-yourself-of-1-year-ago-some-advice-what-would-it-be">Finally, if you could give yourself of 1 year ago some advice, what would it be?</h2>
<blockquote>
<p><strong>Keep things in perspective by appreciating the small wins.</strong></p>
</blockquote>
<p>At times, our industry can be stressful and feel like it's moving at lightning speed, especially for early career developers. You feel like you're going nowhere. Maybe feel like you don't know what you're doing or if you belong where you are.</p>
<p>But keep things in perspective and take small steps. </p>
<ul>
<li>Did you avoid making a mistake you recently made?</li>
<li>Did you ask for help with something you struggled with?</li>
<li>Are you proud of something you built?</li>
</ul>
<p>Each positive small step you take should be valued and moves you closer to where you want to be. It won't happen over night, even over a year. Keep things in perspective and move forward!</p>
<h2 id="heading-the-end">The End</h2>
<p>That's all 30 questions covered. I can't believe I've already made it through the first year and I think it went about as well as I could have hoped for.</p>
<p>If you have any questions for me that I didn't cover already, leave them in the comments and I'll answer them.</p>
<p>Until next time 👋 </p>
]]></content:encoded></item><item><title><![CDATA[A month in my developer life: No.2 - June & July 2021]]></title><description><![CDATA[Welcome back to the monthly (or bi-monthly apparently) insight into my life as a recently employed Full Stack Developer in my first full-time role. It's a chance for me to reflect honestly on my progress and to throw out my thoughts on 'paper'. I hop...]]></description><link>https://blog.kieranroberts.dev/a-month-in-my-developer-life-no2-june-and-july-2021</link><guid isPermaLink="true">https://blog.kieranroberts.dev/a-month-in-my-developer-life-no2-june-and-july-2021</guid><category><![CDATA[Hashnode]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[General Advice]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Mon, 30 Aug 2021 08:55:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1629365739715/h0Spnz37e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back to the monthly (or bi-monthly apparently) insight into my life as a recently employed Full Stack Developer in my first full-time role. It's a chance for me to reflect honestly on my progress and to throw out my thoughts on 'paper'. I hope it can help you too!</p>
<p>If you haven't already noticed, I missed last month's edition so I'm bundling it into this one. Without further delays, let's see how things went for me in months 2 and 3!</p>
<hr />
<h2 id="heading-content">Content</h2>
<ol>
<li><a class="post-section-overview" href="#becoming-more-proactive">Becoming more proactive</a></li>
<li><a class="post-section-overview" href="#making-code-gains">Making code gains</a></li>
<li><a class="post-section-overview" href="#what-kind-of-things-did-i-work-on">What kind of things did I work on?</a></li>
<li><a class="post-section-overview" href="#my-tip-for-the-month">My tip for the month</a></li>
<li><a class="post-section-overview" href="#did-i-achieve-what-i-set-out-to">Did I achieve what I set out to?</a></li>
</ol>
<hr />
<p><strong>Perks of the job!</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629388494991/eaQg_KJA_.jpeg" alt="Collection of Hashnode swag" /></p>
<h2 id="heading-becoming-more-proactive">👊 Becoming more proactive</h2>
<p>I'm not ashamed to say that in my first month, I was very much working reactively as opposed to being proactive. What I mean by this is that I was generally being given for tasks as opposed to picking things up myself. Of course, I was new in my first role and very much trying to find my feet. I didn't want to go and do things I maybe shouldn't.</p>
<p>I was also in the process of becoming comfortable in the multiple codebases we have but there was also a definite lack of confidence on my part. I guess I didn't feel like I should at that point.</p>
<p>In June I felt this started to change.</p>
<p>A week focused on bug fixing forced me into having to pick up tasks myself and carry them through to completion. I'm grateful for this because it broke me out of my shell and I was now feeling like less of a burden on my teammates which I really didn't like.</p>
<p>Now I'm much happier taking on tasks that I don't feel comfortable with. That is the nature of the job after all, especially early on especially at Hashnode where we have to tackle a variety of problems from beginning to end, frontend and backend. This is an awesome thing because I'm able to pick up a lot of things as I go across many different aspects of web development.</p>
<p>I make a note of every bug or problem I come across so that if I ever encounter them again, I'll know what to do.</p>
<h2 id="heading-making-code-gains">💪 Making code gains</h2>
<p>I know I still have a long way to go but I feel as though I made some big strides in these couple of months. Just getting to spend more time in the codebase, reading my coworker's code, writing more code myself. This kind of experience is invaluable.</p>
<p>Of course I still have a long way to go and I'm excited about it. When I first started this job, I looked back at some of the projects I had built previously. Even then I was starting to think that some of that code could be much improved. Just goes to show how much progress you're making when you can realize this and know how you might update it or do things differently.</p>
<h2 id="heading-what-kind-of-things-did-i-work-on">💻 What kind of things did I work on?</h2>
<p>Now that it's not a secret anymore, I can tell you that I was helping out the team on Hashnode's migration to Vercel which you can (and should) read about here 👇 </p>
<p><a target="_blank" href="https://townhall.hashnode.com/powerful-and-superfast-hashnode-blogs-now-powered-by-nextjs-11-and-vercel">Powerful and superfast Hashnode Blogs, now powered by Next.js 11 and Vercel</a></p>
<p>I joined Hashnode shortly after work on the migration had begun and so this was the start of my career in software development. It was of course, my first experience of migrating an existing codebase and in some way I think this helped me get settled. Allowing me to ease me into things since I was mostly migrating and updating existing code instead of writing new code.</p>
<p>It allowed me to fully explore the codebases and find my feet. Things become so much easier in a large codebase when you start figuring out where things are and how things are currently working.</p>
<p>Once the main development work was done on the migration I got to move into some other tasks as well including 👇</p>
<ul>
<li>Unit testing with Jest and end-to-end testing with Cypress.</li>
<li>Implement the new blogs mobile navigation</li>
<li>Several small bug fixes</li>
</ul>
<p>Practical learning (learning by doing) is what usually works for me. I had never touched Cypress before I was tasked with it so it's a case of opening the docs and taking it step-by-step. It's awesome to have the opportunity to learn something new and contribute to the project at the same time.</p>
<p>I'm looking forward to picking up new skills in the coming months.</p>
<h2 id="heading-my-tip-for-the-month">💡 My tip for the month</h2>
<p>Take notes!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629447567736/dOr7K5wtz.jpeg" alt="My notepads with a pen" /></p>
<p>Whether you're like me and love to write handwritten notes, or you favor something like Notion (which I also like). If you're working on a project, learning a topic, or whatever else it may be, write down things of note.</p>
<p>Anything you think you might want to refer back to in the future should be included. Some examples of things I tend to write down 👇</p>
<ul>
<li>Daily tasks</li>
<li>Known bugs/solutions, or bugs I fixed</li>
<li>Steps required to finish a task</li>
<li>Any codebase specific details</li>
</ul>
<p>An extra benefit is that many of my blog posts originate from the notes. They are an endless source of learning and ideas (as long as I keep writing them). My notes were vital for me this month!</p>
<h2 id="heading-did-i-achieve-what-i-set-out-to">Did I achieve what I set out to?</h2>
<p>In the previous article of this series, I noted a bunch of things that I wanted to improve upon in the future. What things you might be asking, and how did I do?</p>
<p>One of the points involved my working day structure and how I felt I could be better at scheduling my general daily tasks around work and just get used to remote working in general. Now I feel like I'm getting the hang of it. My day is a little more structured which works for me, including when I take my breaks and start/finish work.</p>
<p>I didn't really manage to keep up the consistency with my articles and Twitter content but honestly, this didn't end up as a priority for me this time around. I took a little time away from writing articles and I felt like I needed that break. It gave me time to focus on my work and come up with ideas for future content.</p>
<p>As someone who has managed to break into development from a community-taught background, I think I'm in a good position to help others make the same jump. I'll be sharing more content over on Twitter related to this topic while the blog will continue to focus on a mix of technical articles and this monthly series.</p>
<h2 id="heading-the-end">The End</h2>
<p>Thank you all for stopping by and reading my article. I hope you enjoyed. If you did feel free to check out some of my other articles. You can reach me <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a>.</p>
<p>See you very soon (hopefully with just August's review this time 😅)!</p>
]]></content:encoded></item><item><title><![CDATA[HTTP Security Headers and how to set them in Next.js]]></title><description><![CDATA[Security headers are a type of HTTP header there to help us describe our site so we can help protect it against various potential attacks.
In this article, I will introduce HTTP headers including various different security headers that you might want...]]></description><link>https://blog.kieranroberts.dev/http-security-headers-and-how-to-set-them-in-nextjs</link><guid isPermaLink="true">https://blog.kieranroberts.dev/http-security-headers-and-how-to-set-them-in-nextjs</guid><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Fri, 06 Aug 2021 09:03:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1627580267644/shAc36fwY.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Security headers are a type of HTTP header there to help us describe our site so we can help protect it against various potential attacks.</p>
<p>In this article, I will introduce HTTP headers including various different security headers that you might want to use. I will also show you how to implement them in a Next.js application.</p>
<p>Let's go!</p>
<hr />
<h2 id="content">Content</h2>
<ol>
<li><a class="post-section-overview" href="#what-is-a-security-header">What is a security header</a></li>
<li><a class="post-section-overview" href="#examples-of-security-headers">Examples of security headers</a></li>
<li><a class="post-section-overview" href="#adding-security-headers-to-a-nextjs-app">Adding headers to a Next.js app</a></li>
<li><a class="post-section-overview" href="#the-end">The end!</a></li>
</ol>
<hr />
<h2 id="what-is-a-security-header">What is a security header</h2>
<p>First, we need to ask the following question.</p>
<blockquote>
<p>What is an HTTP header?</p>
</blockquote>
<p>An HTTP (HyperText Transfer Protocol) header is simply something that we use to pass some extra information with HTTP requests and responses. Think of them as notes that give more context to the main document. Each header is simply a key-value pair separated by a colon.</p>
<p>When you visit a website, the server receives a request from the browser. This is a request for more information about the site you are visiting which is exchanged from the HTTP request headers. The server then responds and the response will include HTTP response headers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1628181947059/E-aMIFcqu-.png" alt="Diagram of the HTTP request and response to and from the server" /></p>
<p>An example of an HTTP header is the 'Authorization' header. It simply authenticates the user agent (software that acts on behalf of the user) with a server.</p>
<pre><code class="lang-a">Authorization: Bearer &lt;Some token&gt;
</code></pre>
<blockquote>
<p>So what is an HTTP security header?</p>
</blockquote>
<p>We use security headers to help describe our sites specifically to protect against various potential attacks. When a user navigates to the site the server will respond with response headers that provide the browser with information on how to handle things.</p>
<p>By adding security headers to the response of our pages, we can minimize the possibility of the following attacks happening to our application.</p>
<ul>
<li>Cross-Site Scripting (XXS)</li>
<li>Clickjacking</li>
<li>Code injection</li>
</ul>
<h2 id="examples-of-security-headers">Examples of security headers</h2>
<p>Let's take a look at some of the important security headers that you may want to implement.</p>
<p>If you're already familiar with security headers, skip ahead to setting them up in Next.js here - <a class="post-section-overview" href="#adding-security-headers-to-a-nextjs-app">Adding security headers to a Next.js app</a></p>
<h3 id="content-security-policy">Content Security Policy</h3>
<p>The Content Security Policy header allows you to set a policy for which domains are approved for executable scripts. It's like setting a policy for children in a video streaming service. You can specify what they can and cannot watch and it's the same idea here.</p>
<p>Sometimes your site will need to execute scripts that come from origins other than your own application. With this header, we can fine-tune exactly which domains should be considered safe or 'whitelisted' and which should not.</p>
<p>We can also control the kind of resources we load into the site in more detail. For example, we could restrict loading scripts to our own domain but allow fonts or images to be loaded from a specific outside source. </p>
<p>Let's take a look at a few examples.</p>
<p>To allow content from any domain as if the policy was not set 👇</p>
<pre><code class="lang-a">Content-Security-Policy: default-src *
</code></pre>
<p>Or set the own origin as the only trusted domain 👇</p>
<pre><code class="lang-a">Content-Security-Policy: default-src 'self'
</code></pre>
<p>You can also specify any other domain other than your own trusted domain 👇</p>
<pre><code class="lang-a">Content-Security-Policy: default-src 'https://someotherdomain.com'
</code></pre>
<p>We can fine-tune our content security policy to set separate policies for resources like images, fonts, styles, media, scripts, and more like this 👇</p>
<pre><code class="lang-a">Content-Security-Policy: default-src 'self'; font-src 'self' 'https://fonts.googleapis.com'; image-src *.somewhere.com; script-src 'self'
</code></pre>
<p>In the above policy, we set the following</p>
<ol>
<li>We set a default source for the content as our site's origin so this will be used for all resources unless otherwise specified.</li>
<li>We allow fonts to be loaded from our own site or from google fonts</li>
<li>Images can be trusted from anywhere that ends in <code>.somewhere.com</code></li>
<li>Scripts are only trusted from our own site</li>
</ol>
<h3 id="permissions-policy-previously-feature-policy">Permissions Policy (Previously 'Feature Policy')</h3>
<p>The recently renamed Permissions Policy security header allows you to control which browser APIs are allowed to be used within your site document or any iframes located in the document. </p>
<p>Things like geolocation, camera, microphone, and many more can all be disabled if you know your site does not require them and hence can reduce the possibility of attacks coming through these avenues as a result.</p>
<pre><code class="lang-a">Permissions-Policy: camera=(); battery=(self); geolocation=(); microphone=('https://somewhere.com')
</code></pre>
<p>In the above example, we set the policies of several browser features.</p>
<ol>
<li>Camera is empty which means we deny the use of video input devices.</li>
<li>Battery status API is allowed within your own domain</li>
<li>Geolocation is empty which means we deny its use</li>
<li>Audio input devices are allowed for the origin stated</li>
</ol>
<h3 id="x-frame-options">X-Frame-Options</h3>
<p>The X-Frame-Options header controls whether or not your site can be loaded within a frame. Allowing this can be dangerous because you leave yourself open to clickjacking attacks. </p>
<p>Clickjacking is an attack that involves tricking the user into clicking something that they believe to be something else. Attackers may disguise elements and the user is unable to see what they are actually clicking on.</p>
<p>We can help protect ourselves against this attack by preventing our site from being loaded within frames.</p>
<p>There are two options for this header </p>
<pre><code class="lang-a">X-Frame-Options: deny
</code></pre>
<p>The recommended option is to deny your site being loaded within frames which is shown above using the 'deny' option.</p>
<pre><code class="lang-a">X-Frame-Options: SAMEORIGIN
</code></pre>
<p>The 'SAMEORIGIN' option allows the site to be loaded within a frame while serving the site is the same as the one in the frame.</p>
<h3 id="x-content-type-options">X-Content-Type-Options</h3>
<p>The X-Content-Type-Option header ensures that MIME (Multipurpose Internet Mail Extensions) types specified in Content-Type are adhered to and we avoid what is known as 'MIME type sniffing'. Browsers might try and determine the MIME type based on the response content instead of what is specified in the Content-Type header.</p>
<p>It only has one option and it prevents the browser from trying to change the Content-Type away from what was declared.</p>
<pre><code class="lang-a">X-Content-Type-Options: nosniff
</code></pre>
<h3 id="referrer-policy">Referrer policy</h3>
<p>With this header, we can control what information is sent when a user navigates from one origin to another. Consider a user that clicks a link on what we will call the original site. This link takes the user to a different domain. When this happens, some information is sent to the new domain. Information about where the user came from.</p>
<p>There are several different options we can set with this header.</p>
<pre><code class="lang-a">Referrer-Policy: origin-when-cross-origin
</code></pre>
<p>The 'origin-when-cross-origin' option sends the path, origin, and query string with a same-origin request from equal protocol levels. An example of an equal protocol level would be from HTTPS to HTTPS.</p>
<p>If the request is cross-origin, only the origin is sent.</p>
<pre><code class="lang-a">Referrer-Policy: strict-origin-when-cross-origin
</code></pre>
<p>The 'strict-origin-when-cross-origin'  option is similar to the previous option. The path, origin, and query string are included with same-origin requests regardless of protocol level.</p>
<p>When making cross-origin requests, only the origin is sent as long as the protocol level is the same and omits the header otherwise.</p>
<pre><code class="lang-a">Referrer-Policy: strict-origin
</code></pre>
<p>With 'strict-origin', only the origin is sent when the protocol level is equal and otherwise omits the header.</p>
<pre><code class="lang-a">Referrer-Policy: no-referrer
</code></pre>
<p>The 'no-referrer' setting will omit the referrer header.</p>
<p>There are further options which you can check out <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy">here</a></p>
<h3 id="strict-transport-security">Strict Transport Security</h3>
<p>With the Strict Transport Security header, you can specify that your site should only be accessed using the HTTPS protocol. Here we can specify the time in seconds that the browser will remember this protocol.</p>
<pre><code class="lang-a">Strict-Transport-Security: max-age=31536000;
</code></pre>
<p>You can also extend the instruction to all subdomains with an optional setting</p>
<pre><code class="lang-a">Strict-Transport-Security: max-age=31536000; includeSubDomains
</code></pre>
<p>You may think that setting up a redirect from 'http://' to 'https://' is enough but attackers could still seize sensitive information. Setting up a strict transport security setting will make this attack far more difficult.</p>
<h2 id="adding-security-headers-to-a-nextjs-app">Adding security headers to a Next.js app</h2>
<p>Now that we've had a look at some security headers, let's quickly implement them in a Next.js app. Also, feel free to explore some of the other security headers available.</p>
<p>In Next.js we can set security headers from a <code>next.config.js</code> file located at the root of your project.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// next.config.js</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-keyword">async</span> headers() {
    <span class="hljs-keyword">return</span> [

    ]
  },
};
</code></pre>
<p>We return an array of headers that we specify inside JavaScript objects. You can choose to apply headers that will be sent with every route request or on a route-by-route basis. Let's set some of the headers that we previously explored.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// next.config.js</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-keyword">async</span> headers() {
    <span class="hljs-keyword">return</span> [
        {
          <span class="hljs-attr">source</span>: <span class="hljs-string">'/(.*)'</span>,
          <span class="hljs-attr">headers</span>: [
            {
              <span class="hljs-attr">key</span>: <span class="hljs-string">'Content-Security-Policy'</span>,
              <span class="hljs-attr">value</span>:
                <span class="hljs-string">"default-src 'self'; font-src 'self' 'https://fonts.googleapis.com'; img-src 'self' *.somewhere.com; script-src 'self'"</span>,
            },
            {
              <span class="hljs-attr">key</span>: <span class="hljs-string">'X-Frame-Options'</span>,
              <span class="hljs-attr">value</span>: <span class="hljs-string">'DENY'</span>,
            },
            {
              <span class="hljs-attr">key</span>: <span class="hljs-string">'X-Content-Type-Options'</span>,
              <span class="hljs-attr">value</span>: <span class="hljs-string">'nosniff'</span>,
            },
            {
              <span class="hljs-attr">key</span>: <span class="hljs-string">'Referrer-Policy'</span>,
              <span class="hljs-attr">value</span>: <span class="hljs-string">'origin-when-cross-origin'</span>,
            },
            {
              <span class="hljs-attr">key</span>: <span class="hljs-string">'Permissions-Policy'</span>,
              <span class="hljs-attr">value</span>: <span class="hljs-string">"camera=(); battery=(self); geolocation=(); microphone=('https://somewhere.com')"</span>,
            },
          ],
        },
      ];
  },
};
</code></pre>
<p>In the above example, we set our security headers for all routes in our site indicated by the source <code>/(.*)</code>. You could also set security headers on a page-by-page basis like this 👇</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// next.config.js</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-keyword">async</span> headers() {
    <span class="hljs-keyword">return</span> [
        {
          <span class="hljs-attr">source</span>: <span class="hljs-string">'/profile'</span>,
          <span class="hljs-attr">headers</span>: [
            {
              <span class="hljs-attr">key</span>: <span class="hljs-string">'Content-Security-Policy'</span>,
              <span class="hljs-attr">value</span>:
                <span class="hljs-string">"default-src 'self'; font-src 'self' 'https://fonts.googleapis.com'; img-src 'self' *.somewhere.com; script-src 'self'"</span>,
            }
          ]
        },
       {
          <span class="hljs-attr">source</span>: <span class="hljs-string">'/blog'</span>,
          <span class="hljs-attr">headers</span>: [
            {
              <span class="hljs-attr">key</span>: <span class="hljs-string">'Content-Security-Policy'</span>,
              <span class="hljs-attr">value</span>:
                <span class="hljs-string">"default-src 'self'"</span>,
            }
          ]
        },
      ];
  },
};
</code></pre>
<p>Now spin up a development server in your Next.js application and navigate to the route after setting your security headers. You should be able to the headers you set in the network tab of the developer console.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627818497315/OwANSZwNV.png" alt="Network tab in developer console showing the security headers we just set" /></p>
<p>Congratulations, your site is now more secure than it was before! Customize the security headers to suit your application as each site has its own specific requirements.</p>
<h2 id="the-end">The end!</h2>
<p>That was a look at HTTP security headers and how we can implement them in a Next.js application. I hope you managed to learn something new.</p>
<p>You can follow me <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a> and if you liked the article, please let me know about it.</p>
<p>Until next time everyone!</p>
]]></content:encoded></item><item><title><![CDATA[What the hell is Conformance? Highlighted in Next.js 11]]></title><description><![CDATA[Next.js 11 is the latest update to the popular React framework and it introduced a bunch of new and exciting features. Also included in the update is something known as Conformance, but what the hell is it and why do we need it? 
Well, you've come to...]]></description><link>https://blog.kieranroberts.dev/what-the-hell-is-conformance-highlighted-in-nextjs-11</link><guid isPermaLink="true">https://blog.kieranroberts.dev/what-the-hell-is-conformance-highlighted-in-nextjs-11</guid><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[eslint]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Fri, 02 Jul 2021 08:53:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638519848051/W5dVx297e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Next.js 11 is the latest update to the popular React framework and it introduced a bunch of new and exciting features. Also included in the update is something known as <strong>Conformance</strong>, but what the hell is it and why do we need it? </p>
<p>Well, you've come to the right place!</p>
<h2 id="heading-what-does-it-mean-to-conform">What does it mean to conform?</h2>
<p>Let's first ask the question, what does it mean to conform to something? </p>
<p>The simple answer is that it means we behave or comply with a set of rules or standards hopefully for the benefit of all involved.</p>
<p>So even before we take a look at Conformance in Next.js, we have this idea of what it might involve.</p>
<h2 id="heading-conformance-in-nextjs">Conformance in Next.js</h2>
<p>Conformance isn't necessarily a feature in itself, but a system. An overriding system of best practices that sets out to solve a particular problem. Those problems are defined below 👇</p>
<blockquote>
<p>Conformance is a system that provides carefully crafted solutions and rules to support optimal loading and Core Web Vitals, with further additions coming to support other quality aspects like security and accessibility <br /><br /> <a target="_blank" href="https://nextjs.org/blog/next-11">https://nextjs.org/blog/next-11</a></p>
</blockquote>
<p>Conformance aims to help developers achieve a strong base standard for these vital website factors without needing to be an expert in everything and keep up with all of the latest standards. This is done by adding solutions directly into the frameworks responsible for building the sites. This means we can spend more time focusing on features and less time on the rest of it without sacrificing anything.</p>
<p>Conformance in Next.js actually comes from the team at Google Web Platforms and Next.js is the first of the framework ecosystem to see a lot of the current optimizations available. If you would like to find out more about the larger effort which Conformance attempts to solve, check out this article <a target="_blank" href="https://web.dev/introducing-aurora/">web.dev - Introducing Aurora</a>.</p>
<p>You may be reading this article and thinking something like this 👇</p>
<blockquote>
<p>Where does conformance stand in relation to a tool like ESLint? How do they differ?</p>
</blockquote>
<p>I was when I was first reading about Conformance.</p>
<p>Simply put, ESLint is one of the tools identified to be an important part of Conformance in frameworks.</p>
<p>ESLint is a tool that statically analyzes your code based on a set of defined rules which helps you maintain certain standards and consistencies across a project. It's a great tool that we should all be using in order to help maintain consistent code quality and best practices.</p>
<p>This is why there is out-of-the-box support for ESLint in Next.js as of this update. You can start a new project with ESLint ready to go or easily add it to an existing project. Let's check it out!</p>
<h2 id="heading-nextjs-11-eslint">Next.js 11 - ESLint</h2>
<p>ESLint can now easily be added to any new or existing Next.js project so let's do it. I'll create a new project with 👇</p>
<pre><code>npx create<span class="hljs-operator">-</span>next<span class="hljs-operator">-</span>app next11<span class="hljs-operator">-</span>conformance
</code></pre><p>I'll draw your attention to two things. First of all, you will see a <code>.eslintrc</code> file at the root of the project with the base configuration. This is where we configure ESLint for our project. The second point is a new <code>"lint": "next lint"</code> script in the <code>package.json</code> file. This is how we can lint our projects and make sure the code is up to standard.</p>
<p>If your existing project does not currently make use of ESlint, add this script and run it. You will be guided on which packages you need to install to get things up and running.</p>
<p>ESLint will also lint your project whenever you run the script <code>next build</code> and errors in the linting process will stop the build.</p>
<p>I won't be diving into a complete set of ESLint rules for a Next.js project in this article, but I am going to explore the new Next.js specific rules that we now have available.</p>
<h2 id="heading-eslint-plugin-next">eslint-plugin-next</h2>
<p>Our ESLint configuration will include a set of new rules specific to Next.js which are provided by the plugin <code>eslint-plugin-next</code> included in your project when you run <code>create-next-app</code>. Let's see it in action.</p>
<p>I will be working inside <code>pages/index.js</code> for the purpose of the following examples.</p>
<p>Firstly, delete everything inside the return statement of the home page and add the following 👇.</p>
<pre><code><span class="hljs-comment">// pages/index.js</span>
export default <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="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>a href<span class="hljs-operator">=</span><span class="hljs-string">"/"</span><span class="hljs-operator">&gt;</span>
        Home
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  )
}
</code></pre><p>Here we are using a regular anchor tag that links to the home page. Next.js provides a built-in <code>&lt;Link /&gt;</code> component that we should utilize whenever we are routing within our own site. This is one of the things that the new ESLint configuration will catch.</p>
<p>Run the lint command</p>
<pre><code><span class="hljs-attribute">yarn</span> lint
</code></pre><p>and you should see the following inside the terminal.</p>
<pre><code>./pages/index.js
6:7  Error: <span class="hljs-keyword">Do</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">use</span> the HTML &lt;a&gt; tag <span class="hljs-keyword">to</span> navigate <span class="hljs-keyword">to</span> /. <span class="hljs-keyword">Use</span> <span class="hljs-keyword">Link</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'next/link'</span> instead. See: https://nextjs.org/docs/messages/<span class="hljs-keyword">no</span>-html-<span class="hljs-keyword">link</span>-<span class="hljs-keyword">for</span>-pages.  @<span class="hljs-keyword">next</span>/<span class="hljs-keyword">next</span>/<span class="hljs-keyword">no</span>-html-<span class="hljs-keyword">link</span>-<span class="hljs-keyword">for</span>-pages

Need <span class="hljs-keyword">to</span> <span class="hljs-keyword">disable</span> <span class="hljs-keyword">some</span> ESLint <span class="hljs-keyword">rules</span>? Learn more here: https://nextjs.org/docs/basic-features/eslint<span class="hljs-comment">#disabling-rules</span>

<span class="hljs-keyword">error</span> Command <span class="hljs-keyword">failed</span> <span class="hljs-keyword">with</span> <span class="hljs-keyword">exit</span> code <span class="hljs-number">1.</span>
info Visit https://yarnpkg.com/en/docs/cli/run <span class="hljs-keyword">for</span> documentation about this command.
</code></pre><p>Let's make the change it suggests and use the <code>&lt;Link /&gt;</code> component instead.</p>
<pre><code><span class="hljs-keyword">import</span> <span class="hljs-title">Link</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'next/link'</span>;

export default <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="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>Link href<span class="hljs-operator">=</span><span class="hljs-string">"/"</span><span class="hljs-operator">&gt;</span>
        Home
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  )
}
</code></pre><p>Now the errors have gone and we are building an app that adheres to certain standards for the benefit of our site and the users that visit it.</p>
<pre><code><span class="hljs-string">✔</span> <span class="hljs-literal">No</span> <span class="hljs-string">ESLint</span> <span class="hljs-string">warnings</span> <span class="hljs-string">or</span> <span class="hljs-string">errors</span>
</code></pre><p>Let's take a look at one more again using the <code>&lt;Link /&gt;</code> component. When we are using this component and wrapping a child component which includes an <code>&lt;a&gt;</code> tag, and that makes use of the <code>href</code> property, we must pass the property <code>passHref</code> to `. Before we do, let's see the problem in action 👇.</p>
<pre><code><span class="hljs-keyword">import</span> <span class="hljs-title">Link</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'next/link'</span>;

const Component <span class="hljs-operator">=</span> React.forwardRef(({ href }, ref) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>p<span class="hljs-operator">&gt;</span>This <span class="hljs-keyword">is</span> my link with some text<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>a href<span class="hljs-operator">=</span>{href} ref<span class="hljs-operator">=</span>{ref}<span class="hljs-operator">&gt;</span>This <span class="hljs-keyword">is</span> the link<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  )
})

export default <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="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>Link href<span class="hljs-operator">=</span><span class="hljs-string">"/"</span><span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span>Component <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  )
}
</code></pre><p>If we don't use <code>passHref</code> on our link then our <code>&lt;a&gt;</code> tag will not receive it. In this case, we should also wrap the component in <code>React.forwardRef</code> because this child component of <code>&lt;Link /&gt;</code> is a functional component. The following is the error we receive by not including it.</p>
<pre><code>./pages<span class="hljs-operator">/</span>index.js
<span class="hljs-number">4</span>:<span class="hljs-number">19</span>  <span class="hljs-built_in">Error</span>: Component definition <span class="hljs-keyword">is</span> missing display name  react<span class="hljs-operator">/</span>display<span class="hljs-operator">-</span>name
<span class="hljs-number">16</span>:<span class="hljs-number">7</span>  Warning: passHref <span class="hljs-keyword">is</span> missing. See https:<span class="hljs-comment">//nextjs.org/docs/messages/link-passhref  @next/next/link-passhref</span>

Need to disable some ESLint rules? Learn more here: https:<span class="hljs-comment">//nextjs.org/docs/basic-features/eslint#disabling-rules</span>

<span class="hljs-function"><span class="hljs-keyword">error</span> <span class="hljs-title">Command</span> <span class="hljs-title">failed</span> <span class="hljs-title">with</span> <span class="hljs-title">exit</span> <span class="hljs-title">code</span> 1.</span>
</code></pre><p>And if we now add <code>passHref</code> to our <code>&lt;Link /&gt;</code>, everything is great again!</p>
<pre><code><span class="hljs-keyword">import</span> <span class="hljs-title">Link</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'next/link'</span>;

<span class="hljs-comment">// eslint-disable-next-line react/display-name</span>
const Component <span class="hljs-operator">=</span> React.forwardRef(({ href }, ref) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>p<span class="hljs-operator">&gt;</span>This <span class="hljs-keyword">is</span> my link with some text<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>a href<span class="hljs-operator">=</span>{href} ref<span class="hljs-operator">=</span>{ref}<span class="hljs-operator">&gt;</span>This <span class="hljs-keyword">is</span> the link<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  )
})

export default <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="hljs-operator">&lt;</span>div<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>Link href<span class="hljs-operator">=</span><span class="hljs-string">"/"</span> passHref<span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span>Component <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>Link<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">&gt;</span>
  )
}
</code></pre><p>All is well again 👏.</p>
<pre><code><span class="hljs-string">✔</span> <span class="hljs-literal">No</span> <span class="hljs-string">ESLint</span> <span class="hljs-string">warnings</span> <span class="hljs-string">or</span> <span class="hljs-string">errors</span>
</code></pre><h2 id="heading-nextjs-specific-rules">Next.js specific rules</h2>
<p>You may be wondering 👇</p>
<blockquote>
<p>What are the full set of current rules pertaining to Next.js?</p>
</blockquote>
<p>Here they are with a helpful overview of each rule.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Rules</td><td>What's the problem and how to deal with it?</td></tr>
</thead>
<tbody>
<tr>
<td>next/google-font-display</td><td><strong>Error</strong>: Using either <code>auto</code>, <code>block</code> or <code>fallback</code> as the <code>display</code> prop value<br /> <strong>Solution</strong>: Use the value <code>optional</code> or turn the rule off</td></tr>
<tr>
<td>next/google-font-preconnect</td><td><strong>Bad</strong>: Not including <code>rel="preconnect"</code> to google font <code>&lt;link&gt;</code> tags<br /> <strong>Good</strong>: Inlcude it to establish a connection and start the process early</td></tr>
<tr>
<td>next/no-page-custom-font</td><td><strong>Error</strong>: Not adding the custom font to the global <code>pages/_document.js</code><br /> <strong>Solutions</strong>: Turn it off if there is a reason to have the font only available on a single page</td></tr>
<tr>
<td>next/no-html-link-for-pages</td><td><strong>Bad</strong>: Avoid using the<code>&lt;a&gt;</code> tag for on-site links<br /> <strong>Good</strong>: Use the built-in <code>&lt;Link /&gt;</code> component from <code>next/link</code> instead</td></tr>
<tr>
<td>next/link-passhref</td><td><strong>Bad</strong>: Not using the passHref prop when necessary<br /> <strong>Good</strong>: Pass it to <code>&lt;Link to="/" passHref /&gt;</code> component when it wraps a custom component that has an <code>&lt;a&gt;</code> tag using this link</td></tr>
<tr>
<td>next/no-css-tags</td><td><strong>Bad</strong>: Using a <code>&lt;link&gt;</code> tag for an external stylesheet<br /> <strong>Good</strong>: Import styling files when required</td></tr>
<tr>
<td>next/no-document-import-in-page</td><td><strong>Bad</strong>: Don't import <code>next/component</code> ouside of <code>pages/_document.js</code><br /> <strong>Good</strong>: Only required inside <code>pages/_document.js</code> to override the default component</td></tr>
<tr>
<td>next/no-title-in-document-head</td><td><strong>Bad</strong>: Don't use the <code>&lt;title&gt;</code> attribute in the head imported from <code>next/document</code> because title is not the same on all pages<br /> <strong>Good</strong>: Import it from <code>next/head</code> and define title here</td></tr>
<tr>
<td>next/no-head-import-in-document</td><td><strong>Bad</strong>: Don't import the next <code>&lt;Head /&gt;</code> component from <code>next/head</code> in <code>pages/_document.js</code><br /> <strong>Good</strong>: Import from <code>next/document</code> to update head across all of the pages</td></tr>
<tr>
<td>next/no-img-element</td><td><strong>Bad</strong>: Avoid using the <code>&lt;img&gt;</code> element.<br /> <strong>Good</strong>: Use the built-in <code>&lt;Image /&gt;</code> component from <code>next/image</code> instead</td></tr>
<tr>
<td>next/no-sync-scripts</td><td><strong>Bad</strong>: Synchronous scripts that can harm your site's performance and load metrics<br /> <strong>Good</strong>: Use methods like defer or async loading</td></tr>
<tr>
<td>next/no-unwanted-polyfillio</td><td><strong>Bad</strong>: Using duplicate polyfills with Polyfill.io that are already built into Next.js<br /> <strong>Good</strong>: Remove the duplicates</td></tr>
</tbody>
</table>
</div><p>As is the case with ESLint rules, you can turn any of these in your ESLint config rules like this 👇.</p>
<pre><code><span class="hljs-comment">// .eslintrc</span>
{
  <span class="hljs-attr">"extends"</span>: [<span class="hljs-string">"next"</span>, <span class="hljs-string">"next/core-web-vitals"</span>],
  <span class="hljs-attr">"rules"</span>: {
    <span class="hljs-attr">"@next/next/font-google-display"</span>: <span class="hljs-string">"off"</span>,
    <span class="hljs-attr">"@next/next/no-page-custom-font"</span>: <span class="hljs-string">"off"</span>
  }
}
</code></pre><p>If you're just getting started with Next.js I would recommend not turning these rules off until you understand why each rule is important and you run into a case where it is not suited to your situation. They are there to help you out.</p>
<h3 id="heading-is-there-more-to-conformance">Is there more to Conformance?</h3>
<p>Yes there is.</p>
<p>ESLint is not the only tool that is considered a part of Conformance for Next.js. TypeScript is another which adds static type definitions to JavaScript and can help highlight conformance rules to the developer. If you've used TypeScript with Next.js I'm sure you will have noticed integrated type checking on built-in Next.js components. Like when you forget to add the <code>href</code> property to the <code>&lt;Link /&gt;</code> component. You will be warned of the problem allowing you to quickly get rid of problems before they even exist.</p>
<p>Conformance is very much still evolving and I'm sure it will all become clearer in time once its ideas are implemented into other frameworks, and tools and features adapt.</p>
<p>To further explore Conformance I suggest checking out this article <a target="_blank" href="https://web.dev/conformance/">web.dev - Conformance for Frameworks</a> by Shubhie Panicker &amp; Houssein Djirdeh.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That was Conformance in its current state and how it is implemented into Next.js. I think it's an interesting topic that warranted a look at now because we are sure to hear more about it in the future. I hope you were able to learn something new!</p>
<p>You can reach me <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a> with any questions or concerns.</p>
<p>Thanks for being here and I'll see you next time 👋</p>
]]></content:encoded></item><item><title><![CDATA[A month in my developer life: Episode 1 - May 2021]]></title><description><![CDATA[👋 Hi everyone and welcome to a new series here on my blog all about reflecting on my experiences each month as a developer new to the industry. This will mostly be a rambling of my thoughts reflecting on the previous month. 
If that sounds good to y...]]></description><link>https://blog.kieranroberts.dev/a-month-in-my-developer-life-episode-1-may-2021</link><guid isPermaLink="true">https://blog.kieranroberts.dev/a-month-in-my-developer-life-episode-1-may-2021</guid><category><![CDATA[Web Development]]></category><category><![CDATA[General Advice]]></category><category><![CDATA[Hashnode]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Wed, 16 Jun 2021 09:16:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1623650768257/n3JTz00eb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>👋 Hi everyone and welcome to a new series here on my blog all about reflecting on my experiences each month as a developer new to the industry. This will mostly be a rambling of my thoughts reflecting on the previous month. </p>
<p>If that sounds good to you then please keep on reading!</p>
<h2 id="series-preface">Series preface</h2>
<p>I wanted to start writing a series of articles talking about my experiences as a developer taking on his first full-time developer role. To give you a little background on me, I started learning to code as many others did around March 2020. I put thousands of hours into learning and last month in May I secured my first full-time role in the industry.</p>
<p>Many others have picked up programming during this time and are now also looking to make a similar breakthrough, or have recently done so themselves. I thought it would great to start a series focusing on my experience and especially the things I struggle with so that it might help others in similar situations.</p>
<p>Last month I became a Full Stack Developer here at Hashnode 😄. It became my preferred place to blog in March and quickly became something I was passionate about. It's awesome to be working to help build the future of Hashnode.</p>
<p>This was the moment I shared my news with everyone 👇.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/Kieran6dev/status/1390703550641152001">https://twitter.com/Kieran6dev/status/1390703550641152001</a></div>
<p>My role with Hashnode is fully remote. It's not only my first full-time web development role but also the first time that I'm working remotely so I think there's a lot I could share as I take on this brand new challenge. </p>
<p>Now that's over, let's get into my first month!</p>
<h2 id="new-is-the-flavour-of-the-month">🙂 New is the flavour of the month</h2>
<ol>
<li>My first month as an employed software developer</li>
<li>My first month as a fully remote worker</li>
<li>New codebases</li>
<li>New teammates</li>
<li>New challenges</li>
</ol>
<p>My first week was very exciting despite also being a little intimidating purely because it was all new to me. Everyone on the team was super welcoming which helped to settle my nerves and I soon began to dig into the codebase. I've worked many jobs in several different industries over the course of my travels so I know how much easier things become when you have a supportive team.</p>
<p>Jumping into someone else's established codebase for the first time kind of feels like what I imagine being pushed off the side of the cliff into a cold pool of water is like. It's shocking at first, then hopefully your instincts kick in and you start to swim to stop yourself from going under 😄.</p>
<p>Fortunately, I felt comfortable with some of the main players in the tech stack that includes Next.js so that helped ease me in. As I worked my way through reading each and every file on the first day I quickly realized that it would be impossible to pick things up straight away and understand the exact significance of each line, file, and function. It takes time for this stuff to come.</p>
<p>🐾 <strong>Small steps</strong></p>
<p>A good place to start is to work your way through each file to get accustomed to the structure of the project. I use a pen and paper to make regular notes of things I can refer back to and I would be lost without it. Think of it as getting used to a new city after you've just moved in. Walk around and take in the highlights. When you need something in the future, you'll at least know where to look!</p>
<p>I can honestly say that working in a codebase that was not written by me has been a complete eye-opener and something I suggest everyone do as soon as possible.</p>
<p>🙏 <strong>It's OK to be nervous</strong></p>
<p>For the first couple of weeks, I definitely felt some nerves which I think is normal. This was the first time I was working in a shared codebase that was rapidly evolving. More than anything I was afraid of making silly mistakes. </p>
<p>My mind would go blank when I had to perform the simplest of actions with Git for example. I didn't want to cause problems that would negatively affect others and so I would second guess everything I was doing. There were a couple of days where I genuinely couldn't remember how to do simple fetching, merging, and branching because I was second-guessing myself. Even though this was stuff I knew how to do.</p>
<p>By week 2 or 3, I started to calm down and I tried to remember that I did know what I was doing and I was here for a reason. I'm sure this a very common occurrence among new developers and feel free to share it if you have experienced something similar.</p>
<h2 id="getting-used-to-remote-working">🎥 Getting used to remote working</h2>
<p>My current temporary setup, not so fancy but it's mine 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1623938513669/N55pfGpqH.jpeg" alt="My temporary setup including monitor, laptop, and Hashnode mug" /></p>
<p>I've never worked remotely before and I found this to be a surprisingly difficult thing to get accustomed to. All of a sudden you have to think about things like when to take breaks, what work schedule works well for you, and handling regular video calls.</p>
<p>I think it's important to define your own working day structure with some kind of plan. I didn't do this so well for the first two weeks. Some mornings I would forget to eat breakfast until it was almost lunch and I wasn't consistent with taking breaks when I needed them. Weeks 3 &amp; 4 were a little better and I'm starting to see what times work well for those short breaks.</p>
<p><strong>It's all trial and error</strong></p>
<p>I still struggle with remembering to get up and move around. From what I've found, it's easier to feel a little too comfortable in your chair when you're at home which means you forget to get up and take a quick refresher. There was a day last week where I must have been glued to my chair because I completely forgot to move for most of the day 😄. Big mistake! </p>
<p>For the next month, I'm going to dedicate a little break time each day to a small walk or exercise of some kind to help keep me active. I'll share how I get on with this next month.</p>
<p>The other important part of remote working is video calls. I've never been someone who is completely comfortable talking over the phone or over video. Often one of those people who would avoid phone calls at all costs and would much prefer speaking in person. Having now finished my first month I can say that it's getting a little easier but I've still got some ways to go to become one with the camera!</p>
<h2 id="i-get-to-work-on-a-platform-i-use-daily">🤗 I get to work on a platform I use daily!</h2>
<p>It is a really exciting thing to be able to contribute to a product you know and love and I'm extremely fortunate to be in this position. Every time you see one of your pull requests get merged to the big boy branches it feels awesome knowing you were responsible for something, no matter how big or small 😅.</p>
<p>It meant that it often didn't feel like work at all. I was a regular user of Hashnode before I joined the team and it's really interesting to see the inner workings of how it all comes together and where it's headed in the future.</p>
<p>I honestly don't know where that first month went because I loved every second of it, even in my struggles. Blink and you miss it!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1623938617535/_jHri7kLN.jpeg" alt="Hashnode hoodie, Hashnode t-shirt and Hashnode mug" /></p>
<p>Hashnode swag that I was sent this month 👆</p>
<h2 id="maintaining-consistent-content-outside-of-work-is-tough">📝 Maintaining consistent content outside of work is tough</h2>
<p>Starting a new full-time job definitely contributed to a drop in my own content which is to be expected. My tweets were way down and my articles were not as consistent as they were. It's been difficult trying to balance the two things. Some days you don't feel like posting and it's on days like this where having a plan is needed which I was lacking.</p>
<p>For the coming month, I will try and be a little more organized by planning ahead with content a little better so that I don't end up with as many blank days. I love interacting with the community and I feel disappointed in myself when I can't manage it how I would like to.</p>
<h2 id="my-tip-for-the-month">💁‍♂️ My tip for the month</h2>
<p>Every month I will try and offer up a tip of some kind based on my experiences for the month. This month's tip relates to finding your ideal role 👇.</p>
<blockquote>
<p>Build personal projects that represent the field or tech you are really interested in.</p>
</blockquote>
<p>Let's say you are passionate about e-commerce and you would love to work on e-commerce projects in the future. Then build some of your own e-commerce projects and get them out in the open. This can go a long way especially if you are looking to break into the field for the first time.</p>
<p>Doing this will provide the opportunity for employers to contact you first. Share your projects in developer communities and on social platforms and write about them too. Your projects can act as another recruiter for you and you can use them to back up your own claims and knowledge when you get to the interview stage.</p>
<h2 id="the-month-ahead">✌️ The month ahead</h2>
<p>Overall I'm really happy with how my first month as a software developer turned out despite the article focusing on a lot of the things I struggled with. It's just how my brain works, to keep finding things I could get better at. I know there's only one way to go for me and that's ⬆.</p>
<p>Although touches of imposter syndrome crept in at times, I was able to cope with it and I now know that I am able to do this.</p>
<p>Please let me know if you enjoyed reading this article. You can do so in the comments below or <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a> where I'm always available.</p>
<p>Here's to the month ahead! 👋</p>
]]></content:encoded></item><item><title><![CDATA[Let's build a serverless Next.js app with Auth0 for authenticating users]]></title><description><![CDATA[⚠
This article was written in 2021, some of the content may be outdated when you read it.


Let's explore how we can integrate user authentication in a serverless Next.js app using Auth0 in a really simple way. We will be using a package called @auth...]]></description><link>https://blog.kieranroberts.dev/lets-build-a-serverless-nextjs-app-with-auth0-for-authenticating-users</link><guid isPermaLink="true">https://blog.kieranroberts.dev/lets-build-a-serverless-nextjs-app-with-auth0-for-authenticating-users</guid><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[authentication]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Fri, 04 Jun 2021 07:47:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1622745848811/Z5s6NJpyK.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div data-node-type="callout">
<div data-node-type="callout-emoji">⚠</div>
<div data-node-type="callout-text">This article was written in 2021, some of the content may be outdated when you read it.</div>
</div>

<p>Let's explore how we can integrate user authentication in a serverless Next.js app using Auth0 in a really simple way. We will be using a package called <code>@auth0/nextjs-auth0</code> which was built to support user authentication in Next.js using Auth0 and will get us up and running in no time.</p>
<p>One of my recent side projects was building a full-stack serverless Next.js e-commerce site. This site included user authentication and you may be thinking</p>
<blockquote>
<p>How does user authentication work in a serverless Next.js app?</p>
</blockquote>
<p>Good question! Let's answer this by building a simple app that allows users to login/logout and visualize their profile data as a result 👏.</p>
<h2 id="heading-content">Content</h2>
<ol>
<li><p>What is the authentication flow?</p>
</li>
<li><p>Finished app demo</p>
</li>
<li><p>Building our app!</p>
</li>
</ol>
<ul>
<li><p>Initialize the project</p>
</li>
<li><p>Auth0 setup</p>
</li>
<li><p>Auth API route</p>
</li>
<li><p>Auth Provider <code>_app.js</code></p>
</li>
<li><p>Home page</p>
</li>
<li><p><code>useUser()</code> hook</p>
</li>
<li><p>Nav component</p>
</li>
<li><p>AuthLink component</p>
</li>
<li><p>Profile page</p>
</li>
<li><p>Custom auth helpers</p>
</li>
</ul>
<ol start="4">
<li>Conclusion</li>
</ol>
<h2 id="heading-what-is-the-authentication-flow">What is the authentication flow?</h2>
<p>Before we start writing any code I think it's important to understand what exactly we are trying to do and how authentication will work.</p>
<p>This article below will give you a fantastic overview of the approaches you can take to implement Auth0 authentication in a Next.js app.</p>
<p><a target="_blank" href="https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/">Auth0 - The Ultimate Guide to Next.js Authentication with Auth0</a></p>
<p>We are going to take the approach of a serverless application where we instead opt to use functions in our Next.js app that provide us with functionality like authentication when it's needed. We can use our serverless functions on a need-to-use basis as opposed to having a server that is constantly running.</p>
<p>Our application login flow will look something like this 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1622010873910/C7ESZrI6K.png" alt="Our login flow demonstrated in a flowchart" /></p>
<p>When the unauthenticated user clicks the login button they are redirected to the login form provided by Auth0. Then the user proceeds to login and when successful, is redirected back to our app at a destination of our choosing.</p>
<p>It is important to note that I will not be implementing any kind of persistent storage for users in this tutorial. We have no need for a database in a simple app like this and the authentication works nonetheless. Let me know if you want to see an article on integrating a database into the app 😀.</p>
<h2 id="heading-finished-app-demo">Finished app demo</h2>
<p>I thought it would be a good idea to include a quick demo of the finished app before we start so you can visualize what we are going to be building.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://player.vimeo.com/video/557139230?width=1000&amp;height=500">https://player.vimeo.com/video/557139230?width=1000&amp;height=500</a></div>
<p> </p>
<p>The basic features of the app include:</p>
<ul>
<li><p>Home page that instructs the unauthenticated user to login</p>
</li>
<li><p>Protected profile page populated with some of the authenticated user's data</p>
</li>
<li><p>Profile page is a dynamic route <code>/profile/username</code></p>
</li>
<li><p>If an unauthenticated user visits profile page, they will be redirected to login page</p>
</li>
<li><p>Offer logout option for authenticated users</p>
</li>
</ul>
<p>If you want to check out the full repo for this project you can do so here 👇</p>
<p><a target="_blank" href="https://github.com/kieran6roberts/Blog-Next-Auth0">kieran6roberts/Blog-Next-Auth0</a></p>
<h2 id="heading-building-our-app">Building our app!</h2>
<p>To handle our app's authentication we are going to use a package called <code>@auth0/nextjs-auth0</code>. This package was built to facilitate user authentication with Auth0 in a Next.js app. To use it you must satisfy the following conditions.</p>
<ul>
<li><p>Node.js version ^10.13.0 || &gt;=12.0.0</p>
</li>
<li><p>Next.js version &gt;=10</p>
</li>
</ul>
<h3 id="heading-initialize-the-project">Initialize the project</h3>
<p>We'll start this project from scratch so you can see exactly what it takes from start to finish. Start by initializing a Next.js app.</p>
<pre><code class="lang-markdown">npx create-next-app next-auth0
</code></pre>
<p>Now install the necessary packages for our app</p>
<pre><code class="lang-markdown">yarn add @auth0/nextjs-auth0 @emotion/react
</code></pre>
<ul>
<li><p><code>@auth0/nextjs-auth0</code> - SDK for handling authentication in our app</p>
</li>
<li><p><code>@emotion/react</code> - Emotion CSS in JS react package for styling</p>
</li>
</ul>
<p>I'll be using Emotion for styling in this app but you can use whatever you want, it's not important, is easily substituted, and there won't be much styling anyway.</p>
<h3 id="heading-auth0-setup">Auth0 setup</h3>
<p>Before continuing with the code, we first need to set up a new application with Auth0 on their site. You can do so here <a target="_blank" href="https://auth0.com/signup?place=header&amp;type=button&amp;text=sign%20up">Sign Up - Auth0</a> or simply login if you already have an account.</p>
<p>Then we are going to set up a new application in the Auth0 dashboard. Once you've logged in, navigate to the 'Applications' tab.</p>
<p>Click the 'Create Application' button and give the app a name. Here I name mine 'my-next-auth0'. Then select the 'Regular Web Application' option.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621680905988/woAAnE0mu.png" alt="Creating our app in the auth0 dashboard" /></p>
<p>When prompted select 'Next.js' as the technology for the application. Auth0 recently had an update to their site and now offer some very helpful documentation on how to get started with Next.js and Auth0 authentication. This information is available in the 'Quick Start' tab of your application if you ever need any help.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621681300190/CtE9wLjP7.png" alt="Auth0 application quick start tab with helpful guides" /></p>
<p>The next step is to configure some settings in our Auth0 application. Click on the 'Settings' tab and here we need to set two options.</p>
<ol>
<li><p>Allowed Callback URLs - <code>http://localhost:3000/api/auth/callback</code></p>
</li>
<li><p>Allowed Logout URLs - <code>http://localhost:3000/</code></p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621857027732/JuXfBMlJw.png" alt="Setting our auth0 application allowed callback URL and logout URL" /></p>
<p>You might choose to configure more settings depending on your app but this is enough for us. If you end up deploying the app then you would also want to come back in here and add your new URLs.</p>
<p>When we as a user have entered our login details successfully, Auth0 will need a destination to send the user. This is why we specify our API callback route so that our auth package can handle the redirect.</p>
<p>When the user hits the logout button, we also specify the url's that are valid for auth0 to redirect to for the logout case. Here it is <code>http://localhost:3000/</code>.</p>
<p>The package <code>@auth0/nextjs-auth0</code> also requires the following settings within the Auth0 app dashboard so make sure to check they are correct.</p>
<ol>
<li><p>Json Web Token Signature Algorithm - RS256</p>
</li>
<li><p>OIDC Conformant - True</p>
</li>
</ol>
<p>You can find those settings in your applications 'Advanced Settings - OAuth'.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621857158135/eukDwM4aY.png" alt="auth0 app dahsboard advanced settings" /></p>
<p>Now that we have our settings in place we are going to need some constants in our Next.js app which we will store in environment variables. Create a <code>.env.local</code> file at the root of your app:</p>
<pre><code class="lang-markdown">// at the project root
touch .env.local
</code></pre>
<p>and add the following but replace the examples with your settings 👇</p>
<pre><code class="lang-markdown">AUTH0<span class="hljs-emphasis">_BASE_</span>URL={{Base URL of your site, probably http://localhost:3000 in development}}
AUTH0<span class="hljs-emphasis">_ISSUER_</span>BASE<span class="hljs-emphasis">_URL={{https://your auth app domain found in dashboard}}
AUTH0_</span>SECRET={{Some secret used to encrypt the session cookie}}
AUTH0<span class="hljs-emphasis">_DOMAIN={{https://your auth app domain found in dashboard}}
AUTH0_</span>CLIENT<span class="hljs-emphasis">_ID={{Auth0 app client id found in dashboard}}
AUTH0_</span>CLIENT<span class="hljs-emphasis">_SECRET={{Auth0 app client secret in dashboard}}
AUTH0_</span>SCOPE={{The scopes we want to give to our authorized users, here I will use 'openid profile'}}
</code></pre>
<p>Our auth package will pull the following information from your<code>.env.local</code> file so make sure you don't miss these out and that you also name them like this 👆.</p>
<p>It's up to you to locate the missing information from your dashboard and add it to the <code>.env.local</code> file and I've added a description for each variable to help you along. These settings should be secret to your app so don't go sharing them with anyone 😉.</p>
<p>For the <code>AUTH0_SECRET</code> variable you can generate a secure secret from the command line using:</p>
<pre><code class="lang-markdown">openssl rand -hex 32
</code></pre>
<h3 id="heading-auth-api-route">Auth API route</h3>
<p>Now our application is created and our variables are setup we can start working on the authentication flow in our Next.js app.</p>
<p>To do this we are going to make use of Next.js API routes. If you need a quick introduction to these routes, check out my article here <a target="_blank" href="https://blog.kieranroberts.dev/getting-started-with-nextjs-api-routes">Getting started with Next.js API routes</a>.</p>
<p>We will need one API route to handle user authentication. Create the file <code>pages/api/auth/[...auth0].js</code>.</p>
<pre><code class="lang-markdown">// project root
touch pages/api/auth/[...auth0].js
</code></pre>
<p>Using this dynamic route will match all paths prefaced by <code>/api/auth/</code> meaning it will run whenever we hit one of our auth routes. The <code>handleAuth()</code> function which you are about to see will create the following routes for us seemingly by magic 🪄.</p>
<ul>
<li><p><code>/api/auth/login</code></p>
</li>
<li><p><code>/api/auth/callback</code></p>
</li>
<li><p><code>/api/auth/logout</code></p>
</li>
<li><p><code>/api/auth/me</code></p>
</li>
</ul>
<pre><code class="lang-markdown">import { handleAuth } from '@auth0/nextjs-auth0';

export default handleAuth();
</code></pre>
<p>We will explore how we can add some custom functionality to this later but for now that's all you need.</p>
<h3 id="heading-auth-providerappjs">Auth Provider<code>_app.js</code></h3>
<p>We can access the user on the client or on the server. To access the user on the client we must wrap our app in the <code>UserProvider</code> component provided by <code>@auth0/nextjs-auth0</code>.</p>
<p>In our app we are going to be accessing the user on the server. Despite this, I will add the provider to the app in case something changes and we need access to the user on the client.</p>
<p>Inside the <code>_app.js</code> page you should wrap the returned content with the provider.</p>
<pre><code class="lang-markdown">// <span class="hljs-emphasis">_app.js
import { Global, css } from '@emotion/react';
import { UserProvider } from '@auth0/nextjs-auth0';

function MyApp({ Component, pageProps }) {
  return (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">UserProvider</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Global</span> <span class="hljs-attr">styles</span>=<span class="hljs-string">{css</span>`
        *, *<span class="hljs-attr">::after</span>, *<span class="hljs-attr">::before</span> {
          <span class="hljs-attr">box-sizing:</span> <span class="hljs-attr">border-box</span>;
          <span class="hljs-attr">font-family:</span> '<span class="hljs-attr">Gill</span> <span class="hljs-attr">Sans</span>', '<span class="hljs-attr">Gill</span> <span class="hljs-attr">Sans</span> <span class="hljs-attr">MT</span>', <span class="hljs-attr">Calibri</span>, '<span class="hljs-attr">Trebuchet</span> <span class="hljs-attr">MS</span>', <span class="hljs-attr">sans-serif</span>;
          <span class="hljs-attr">padding:</span> <span class="hljs-attr">0</span>;
          <span class="hljs-attr">margin:</span> <span class="hljs-attr">0</span>;
        }

        <span class="hljs-attr">a</span> {
          <span class="hljs-attr">text-decoration:</span> <span class="hljs-attr">none</span>;
        }

        <span class="hljs-attr">ul</span> {
          <span class="hljs-attr">list-style:</span> <span class="hljs-attr">none</span>;
        }
      `}
      /&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Component</span> {<span class="hljs-attr">...pageProps</span>} /&gt;</span></span>
    <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">UserProvider</span>&gt;</span></span>
  )
}

export default MyApp</span>
</code></pre>
<p>I have also added some basic global style resets which you are more than welcome to expand upon.</p>
<p>That is the simple setup complete for handling our user authentication. I promise that is not a joke. That's all you need! Soon we will see what else we can do but for now, our user authentication is functional!</p>
<p>The next step is to build out our application UI so we can show the user a login or logout link depending on whether the user is authenticated or not. We will also add some of the user's dynamic data when they are authenticated and work with protected routes.</p>
<h3 id="heading-home-page">Home page</h3>
<p>Let's start with the home page. For an unauthenticated user it will look like this👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1621780933575/8tc37Bnp_.png" alt="Our app home page screenshot with unauthenticated user" /></p>
<p>For an authenticated user it will look like this 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1622460480081/3IyMDo-Q_Q.png" alt="Our app home page with authenticated user" /></p>
<p>So let's write the code for it.</p>
<pre><code class="lang-markdown">/<span class="hljs-strong">** @jsxRuntime classic /
/**</span> @jsx jsx <span class="hljs-emphasis">*/
import { css, jsx } from '@emotion/react';
import { getSession } from '@auth0/nextjs-auth0';

import AuthLink from '../components/AuthLink';
import Nav from '../components/Nav';

export default function Home({ user }) {
  return (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`
      <span class="hljs-attr">align-items:</span> <span class="hljs-attr">center</span>;
      <span class="hljs-attr">background:</span> #<span class="hljs-attr">ff00cc</span>;
      <span class="hljs-attr">background:</span> <span class="hljs-attr">-webkit-linear-gradient</span>(<span class="hljs-attr">to</span> <span class="hljs-attr">right</span>, #<span class="hljs-attr">333399</span>, #<span class="hljs-attr">ff00cc</span>);
      <span class="hljs-attr">background:</span> <span class="hljs-attr">linear-gradient</span>(<span class="hljs-attr">to</span> <span class="hljs-attr">right</span>, #<span class="hljs-attr">333399</span>, #<span class="hljs-attr">ff00cc</span>);
      <span class="hljs-attr">display:</span> <span class="hljs-attr">flex</span>;
      <span class="hljs-attr">height:</span> <span class="hljs-attr">100vh</span>;
      <span class="hljs-attr">justify-content:</span> <span class="hljs-attr">center</span>;
    `}
    &gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`
        <span class="hljs-attr">background-color:</span> #<span class="hljs-attr">fff</span>;
        <span class="hljs-attr">border-radius:</span> <span class="hljs-attr">1rem</span>;
        <span class="hljs-attr">padding:</span> <span class="hljs-attr">4rem</span> <span class="hljs-attr">8rem</span>;
        <span class="hljs-attr">text-align:</span> <span class="hljs-attr">center</span>;
        `}
      &gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Nav</span> <span class="hljs-attr">user</span>=<span class="hljs-string">{user}</span> /&gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`
          <span class="hljs-attr">margin:</span> <span class="hljs-attr">2rem</span> <span class="hljs-attr">0</span>;
        `}
        &gt;</span></span>
          {user?.name ?? 'Welcome User'}
        <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span> &gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`
          <span class="hljs-attr">margin-top:</span> <span class="hljs-attr">2rem</span>;
          <span class="hljs-attr">margin-bottom:</span> <span class="hljs-attr">3rem</span>;
        `}
        &gt;</span></span>
          {user ? `It's great to have you here${user.given_name ? ` ${user.given_name}!!` : '!!'}` : 'I bet you would like to sign in to our app right? Click the Sign in button below'}
        <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span></span>
          {user ?
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AuthLink</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/api/auth/logout"</span>&gt;</span></span>Sign Out<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">AuthLink</span>&gt;</span></span>
          :
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AuthLink</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/api/auth/login"</span>&gt;</span></span>Sign In<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">AuthLink</span>&gt;</span></span>
          }
        <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}

export async function getServerSideProps(ctx) {
  const { req, res } = ctx;
  const session = getSession(req, res);

  return {
    props: { user: session?.user ?? null }
  }
}</span>
</code></pre>
<p>As you can see it is a fairly straightforward page, but I will draw your attention to the main points.</p>
<p>First of all, we are pre-rendering our page on the server using <code>getServerSideProps()</code>. We use the helper function <code>getSession()</code> which takes the request and response objects as parameters and retrieves the user's current session if it exists and otherwise null. This session will contain the user data as a property on the session object.</p>
<p>The main reason I am server-rendering this page is to know for sure whether we have a user before rendering the page. Our user profile route which we will soon implement will be a dynamic route that will look like <code>/profile/username</code>. We need the user object to create this route so we pre-fetch this user on the server.</p>
<p>You'll also notice that I'm importing two components <code>Nav</code> and <code>AuthLink</code> in our home page. This is because we will also need the same markup for our soon-to-be Profile page so I broke it down into the reusable components that we will soon see.</p>
<h3 id="heading-useuser-hook"><code>useUser()</code> hook</h3>
<p>Another method of fetching the user with this package is through the <code>useUser()</code> hook. This is the method you would use if you wanted access to the user on the client and would be possible for us to implement this in our app since we previously set up the <code>UserProvider</code> component.</p>
<p>It lets us hook into the user so that the client is aware of whether or not we have an authenticated user. We can make use of <code>loading</code>, <code>data</code>, and <code>error</code> states to conditionally render data based on the outcomes. This is how you would implement the hook 👇.</p>
<pre><code class="lang-markdown">import { useUser } from '@auth0/nextjs-auth0';

// Inside your component
const { user, error, loading } = useUser();
</code></pre>
<p>This does not mean authentication takes place on the client. Our authentication would still take place on the server and no sensitive information is exposed.</p>
<h3 id="heading-nav-component">Nav component</h3>
<p>This is our <code>Nav</code> component which is just a simple set of links with some styling 👇</p>
<pre><code class="lang-markdown">/<span class="hljs-strong">** @jsxRuntime classic /
/**</span> @jsx jsx <span class="hljs-emphasis">*/
import { css, jsx } from '@emotion/react';

const linkItem = css`margin: 0 2rem;`;
const link = css`color: #ff00cc; padding: 0.5rem 1rem;`;

const Nav = ({ user }) =&gt; {
  return (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`
        <span class="hljs-attr">display:</span> <span class="hljs-attr">flex</span>;
        <span class="hljs-attr">justify-content:</span> <span class="hljs-attr">center</span>;
        <span class="hljs-attr">margin-bottom:</span> <span class="hljs-attr">1rem</span>;
      `}
      &gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{linkItem}</span>&gt;</span></span>
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{link}</span>
          <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span>
          &gt;</span></span>
            Home
          <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{linkItem}</span>&gt;</span></span>
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{link}</span>
          <span class="hljs-attr">href</span>=<span class="hljs-string">{</span>`/<span class="hljs-attr">profile</span>/${<span class="hljs-attr">user</span>?<span class="hljs-attr">.nickname</span> ?? '<span class="hljs-attr">_</span>'}`}
          &gt;</span></span>
            Profile
          <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span>
    <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span>
  )
}

export default Nav;</span>
</code></pre>
<p>The component takes the user object as a prop. If the user exists, we set the route of the profile link to <code>/profile/${user?.nickname ?? '_'}</code>. If the user exists we have a dynamic route with the nickname of the user included. Otherwise, we set it to <code>/profile/_</code> because we still have to set something otherwise we will end up on a 404 route because <code>/profile/</code> does not exist. We will set up a protected route for the profile page.</p>
<h3 id="heading-authlink-component">AuthLink component</h3>
<p>Our <code>Auth Link</code> component is the main call-to-action link for signing users in and out of our app.</p>
<pre><code class="lang-markdown">/<span class="hljs-strong">** @jsxRuntime classic /
/**</span> @jsx jsx <span class="hljs-emphasis">*/
import { css, jsx } from '@emotion/react';

const AuthLinkStyles = css`
  background-color: #ff00cc;
  border: 1px solid #ff00cc;
  border-radius: 0.5rem;
  color: #fff;
  display: block;
  margin: auto;
  padding: 1rem 2rem;
  width: max-content;
`;

const AuthLink = ({ children, path }) =&gt; (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{AuthLinkStyles}</span> <span class="hljs-attr">href</span>=<span class="hljs-string">{path}</span>&gt;</span></span>{children}<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span></span>
);

export default AuthLink;</span>
</code></pre>
<p>We can pass a dynamic path to the component props based on whether the user is authenticated.</p>
<h3 id="heading-profile-page">Profile page</h3>
<p>Now I'm going to create another page at a dynamic route <code>/profile/[profile].js</code> and this should only be accessible to users that are logged in. If an unauthenticated user tries to access this page, they should be redirected to login instead.</p>
<p>Let's remind ourselves of what it looks like 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1622462444459/3UX9O7dxG.png" alt="Screenshot of our app profile page with authenticated user" /></p>
<p>We can implement protected routes using a helper function provided by this package called <code>withPageAuthRequired()</code>. You wrap <code>getServerSideProps()</code> using this function to check whether we have an authenticated user, and if not they will be redirected. If the user exists, there will be an added <code>user</code> object inside the page props object.</p>
<p>Start by creating our profile page file.</p>
<pre><code class="lang-markdown">// project root
touch pages/profile/[profile].js
</code></pre>
<p>Then add the following code 👇</p>
<pre><code class="lang-markdown">/<span class="hljs-strong">** @jsxRuntime classic /
/**</span> @jsx jsx <span class="hljs-emphasis">*/
import { css, jsx } from '@emotion/react';
import { getSession, withPageAuthRequired } from "@auth0/nextjs-auth0";

import AuthLink from '../../components/AuthLink';
import Nav from '../../components/Nav';

const profileItem = css`margin: 2rem 0`;

export default function Profile({ user }) {
  return (
     <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`
      <span class="hljs-attr">align-items:</span> <span class="hljs-attr">center</span>;
      <span class="hljs-attr">background:</span> #<span class="hljs-attr">40E0D0</span>;
      <span class="hljs-attr">background:</span> <span class="hljs-attr">-webkit-linear-gradient</span>(<span class="hljs-attr">to</span> <span class="hljs-attr">right</span>, #<span class="hljs-attr">FF0080</span>, #<span class="hljs-attr">FF8C00</span>, #<span class="hljs-attr">40E0D0</span>);
      <span class="hljs-attr">background:</span> <span class="hljs-attr">linear-gradient</span>(<span class="hljs-attr">to</span> <span class="hljs-attr">right</span>, #<span class="hljs-attr">FF0080</span>, #<span class="hljs-attr">FF8C00</span>, #<span class="hljs-attr">40E0D0</span>);

      <span class="hljs-attr">display:</span> <span class="hljs-attr">flex</span>;
      <span class="hljs-attr">height:</span> <span class="hljs-attr">100vh</span>;
      <span class="hljs-attr">justify-content:</span> <span class="hljs-attr">center</span>;
    `}
    &gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`
      <span class="hljs-attr">background-color:</span> #<span class="hljs-attr">fff</span>;
      <span class="hljs-attr">border-radius:</span> <span class="hljs-attr">1rem</span>;
      <span class="hljs-attr">padding:</span> <span class="hljs-attr">4rem</span> <span class="hljs-attr">8rem</span>;
      <span class="hljs-attr">text-align:</span> <span class="hljs-attr">center</span>;
      `}
      &gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Nav</span> <span class="hljs-attr">user</span>=<span class="hljs-string">{user}</span> /&gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`<span class="hljs-attr">margin:</span> <span class="hljs-attr">2rem</span> <span class="hljs-attr">0</span>`}&gt;</span></span>Welcome to your profile {user.name}<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span></span>
        {user &amp;&amp;
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span>
          <span class="hljs-attr">alt</span>=<span class="hljs-string">"user avatar"</span>
          <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`
          <span class="hljs-attr">border-radius:</span> <span class="hljs-attr">0.5rem</span>
          `}
          <span class="hljs-attr">src</span>=<span class="hljs-string">{user.picture}</span>
          /&gt;</span></span>
        }
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{css</span>`<span class="hljs-attr">margin-bottom:</span> <span class="hljs-attr">2rem</span>`}&gt;</span></span>
        {user.given_name ? <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{profileItem}</span>&gt;</span></span>First name: {user.given_name}<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span> : null}
        {user.family_name ? <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{profileItem}</span>&gt;</span></span>Family name: {user.family_name}<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span> : null}
        {user.nickname ? <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{profileItem}</span>&gt;</span></span>Nickname: {user.nickname}<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span> : null}
        {user.favoriteFood ? <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">css</span>=<span class="hljs-string">{profileItem}</span>&gt;</span></span>Favorite Food: {user.favoriteFood}<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span></span> : null}
        <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">AuthLink</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/api/auth/logout"</span>&gt;</span></span>Sign Out<span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">AuthLink</span>&gt;</span></span>
      <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}

export const getServerSideProps = withPageAuthRequired({
  async getServerSideProps(ctx) {
    const { req, res, query } = ctx;
    const { profile } = query;
    const session = getSession(req, res);

    if (profile !== session.user.nickname) {
      return {
        redirect: {
          destination: `/profile/${session.user.nickname}`,
          permanent: true
        }
      }
    }

    return {
      props: {}
    };
  }
});</span>
</code></pre>
<p>We start with our server-side rendered page that we wrap in our protected route wrapper. We again access the user's session but this time we want to see whether or not the user is trying to access their own profile. In our app want to restrict users to their own profile only.</p>
<p>To do this we check if the session nickname of the user is the same as the nickname query accessed from the context query. If they don't match we will redirect the user back to their own page.</p>
<p><strong>Note 👇</strong></p>
<p>I recognize that this is not a very robust check and if you are implementing a real-world app where profile privacy is essential. If we had a database then we could ensure every user has a unique identifier to compare against but I'm not going to make this article longer than it already is 😂.</p>
<p><strong>End of note</strong></p>
<p>As you can see we access the <code>user</code> object through our page props which will be provided automatically by <code>withPageAuthRequired()</code>.</p>
<h3 id="heading-custom-auth-helpers">Custom auth helpers</h3>
<p>Remember how we earlier had only two lines of code in our API auth route? We can do a lot more in here with helpers provided by our auth package. It provides a set of handlers for each state of the authentication process. They are the following 👇</p>
<ul>
<li><p><code>handleCallback</code></p>
</li>
<li><p><code>handleLogin</code></p>
</li>
<li><p><code>handleLogout</code></p>
</li>
<li><p><code>handleProfile</code></p>
</li>
</ul>
<p>Now I'm going to add them into our API auth route so you can see how to implement them yourself. I will also use the <code>afterCallback</code> function to add a custom property to our user object once authenticated.</p>
<pre><code class="lang-markdown">import {
  handleAuth,
  handleCallback,
  handleLogin,
  handleLogout,
  handleProfile
} from '@auth0/nextjs-auth0';

const afterCallback = (req, res, session, state) =&gt; {
  // user is located in session
  session.user.favoriteFood = 'pizza';
  return session;
};

//Use this to store additional state for the user before they visit the Identity Provider to login.
const getLoginState = (req, loginOptions) =&gt; {
  console.log(loginOptions.authorizationParams.scope); // access scope
  return {}; // custom state value must be an object
};

// Use this to add or remove claims on session updates
const afterRefetch = (req, res, session) =&gt; {};

export default handleAuth({
  async callback(req, res) {
<span class="hljs-code">    try {
      await handleCallback(req, res, { afterCallback });
    } catch (err) {
        res.status(err.status ?? 500).end(err.message)
    }
  },
  async logout(req, res) {
    try {
      await handleLogout(req, res);
    } catch (err) {
        res.status(err.status ?? 500).end(err.message)
    }
  },
  async login(req, res) {
    try {
      await handleLogin(req, res, { getLoginState });
    } catch (err) {
        res.status(err.status ?? 500).end(err.message)
    }
  },
  async me(req, res) {
    try {
      await handleProfile(req, res, { afterRefetch });
    } catch (err) {
        res.status(err.status ?? 500).end(err.message)
    }
  }
});</span>
</code></pre>
<p>We can call these four asynchronous functions inside our <code>handleAuth</code> object and in each we use our handler functions wrapped in a try/catch block. Each of the handlers has a custom set of handlers so consult the <a target="_blank" href="https://auth0.github.io/nextjs-auth0/index.html">documentation</a> if you need help.</p>
<p>I wanted to show that it is possible to add custom functionality to each step based on your requirements. Each time we login, we redirect to our callback function which runs our <code>afterCallback</code> function. In here we add a custom property called <code>favoriteFood</code> on our <code>user</code> object which is accessed through the <code>session</code> object.</p>
<p>Now you should see an extra list item with my favorite food being pizza on the profile page that we previously set up. The app should now resemble the finished app from the video earlier on. Well done 👏.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That brings us to the end of this demonstration of how we can add user authentication to a Next.js app using Auth0. I think you'll agree that it was a reasonably easy thing to set up. The next step might be to set up a database so you can persist users with custom accounts suited to your app.</p>
<p>You have any questions then contact me <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a> where I'd be more than happy to answer you.</p>
<p>If you made it this far congrats 🎉. Until next time friends 👋</p>
]]></content:encoded></item><item><title><![CDATA[Developer portfolio do's & don'ts]]></title><description><![CDATA[So you've now built some projects and you're ready to tackle your developer portfolio, great! Whether you'll need one will depend on your situation but if you're coming from a self-taught background as I was, it's a useful thing to have in your back ...]]></description><link>https://blog.kieranroberts.dev/developer-portfolio-dos-and-donts</link><guid isPermaLink="true">https://blog.kieranroberts.dev/developer-portfolio-dos-and-donts</guid><category><![CDATA[Web Development]]></category><category><![CDATA[portfolio]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Fri, 21 May 2021 07:51:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1621576603073/BTfPDYL3m.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So you've now built some projects and you're ready to tackle your developer portfolio, great! Whether you'll need one will depend on your situation but if you're coming from a self-taught background as I was, it's a useful thing to have in your back pocket.</p>
<p>You will also probably need a portfolio if you're looking to start freelancing so that you can show potential clients that you're up to the task. This article won't be focusing on a freelancer's portfolio because it's not my area of knowledge but there is plenty of overlap of useful tips so please keep reading 😄.</p>
<p>Building your own portfolio is not an easy task. There is a lot of different ways you can go about the task and I thought it would be useful to write an article that answers some of the questions I had when I was building mine. </p>
<p>Here are my portfolio do's &amp; don'ts 👏</p>
<h2 id="sections">Sections</h2>
<ol>
<li>📞 Contact Details<ul>
<li><strong>Do</strong> - Make your contact details obvious</li>
<li><strong>Don't</strong> - Add social links to accounts that you don't actually use</li>
</ul>
</li>
<li>🖥 Projects<ul>
<li><strong>Do</strong> - Consider adding project case studies</li>
<li><strong>Do</strong> - Make it easy to jump to the projects</li>
<li><strong>Don't</strong> - Include the portfolio itself as a project if you have enough other projects to show</li>
<li><strong>Do</strong> - If you're going to include tutorial projects, customize them in some way</li>
</ul>
</li>
<li>😃 About Me<ul>
<li><strong>Don't</strong> - Include your life story</li>
<li><strong>Don't</strong> - Include skill bars</li>
</ul>
</li>
<li>✅ Tech stack doesn't matter, performance &amp; accessibility does<ul>
<li><strong>Do</strong> - Ensure the site is performant</li>
<li><strong>Do</strong> - Ensure your site is accessible</li>
</ul>
</li>
<li>Summary</li>
</ol>
<h2 id="1-contact-details">📞 1. Contact Details</h2>
<h3 id="do-make-your-contact-details-obvious">Do - Make your contact details obvious</h3>
<p>Not making your contact details obvious to the hiring employee/potential client is probably the worst thing you can do. Why go to all the trouble of showing off what you can if you don't make it easy for them to contact you afterward?</p>
<p>Consider adding multiple communication options on your site. This could be through email, Twitter, Linkedin, Github, and anything else you actually use and have a presence on. When your site loads, the user should be able to click <strong>one</strong> button and be taken to a contact option.</p>
<p>You could do this using a call-to-action on your hero that says something like 'contact me' taking the user to a contact form on your page. Maybe you could add links to your accounts using icons and add them to the hero so the when the page loads they're right there on the screen.</p>
<p>Whatever it is, make sure it's obvious and simple for the person to find and contact you. You don't want to miss out on an opportunity because you made it difficult for them to reach out. Employers are not going to spend very long on your page and they will just move on if they find it difficult.</p>
<h3 id="dont-add-social-links-to-accounts-that-you-dont-actually-use">Don't - Add social links to accounts that you don't actually use</h3>
<p>I remember when I was building my portfolio for the first time. I searched for what contact links are common for someone to add to a portfolio. Common options include:</p>
<ul>
<li>Email</li>
<li>Github</li>
<li>Twitter</li>
<li>Linkedin</li>
<li>Youtube</li>
<li>Blogging profiles</li>
</ul>
<p>As a developer you should already have a Github profile which can be added along with your email but the rest are usually optional.</p>
<p>When I was making my portfolio I had a bare Twitter account which I had created a few months prior and an almost empty Linkedin profile. If these are not sites you actually intend on using, don't bother including them in your portfolio. What's the use of showing an employer your Twitter account when you don't post there or contribute anything related to web development and the jobs you may be applying for.</p>
<p>My suggestion would be to start using some of them and begin branching out. The word 'networking' made me nervous when I started but it's really not that difficult to start making connections with like-minded people. Then you can add these profiles to your portfolio and start building a more well-rounded portfolio.</p>
<h2 id="2-projects">🖥 2. Projects</h2>
<h3 id="do-consider-adding-project-case-studies">Do - Consider adding project case studies</h3>
<p>There are plenty of great portfolios that don't include case studies of their projects but I think it can help you stand out. You don't need to write thousands of words for each study but adding a little more background to each project can be very insightful.</p>
<p>Tailor each case study to the real world and how your potential employer would look at a project. Consider adding the following elements to the study 👇</p>
<ul>
<li>Project objectives </li>
<li>Why the stack choice was optimal for achieving the objective</li>
<li>Some of the problems you faced and how you overcame them</li>
<li>What you might do differently next time</li>
</ul>
<p>Showing potential employers that you considered the trade-offs between alternative solutions and tech, based on the needs of the client (or you for a personal project) will go a long way. </p>
<p>Instead of saying </p>
<blockquote>
<p>The front-end of this project was built with the JavaScript library React.</p>
</blockquote>
<p>Expand on it by saying why this decision was taken with something like this 👇</p>
<blockquote>
<p>I decided to go with React for this project because there is a lot of state management required which would be much more difficult to maintain without the JavaScript library. By choosing React I could get the live site up and running faster meaning the client could start trading as early as possible. It would also provide a more maintainable codebase for the future...</p>
</blockquote>
<h3 id="do-make-it-easy-to-jump-to-the-projects">Do - Make it easy to jump to the projects</h3>
<p>Your portfolio is essentially a house to show off your projects right. Almost like when you want to buy a car. You go to the showroom or garage and the employee walks you around to see some of the cars on show. You're not really interested in anything else but cars. If you like what you see you input your contact details and make a purchase.</p>
<p>The same goes for the portfolio. Make it easy for the user to find the projects so they can see what you can do and then contact you if applicable. How you do this is up to you and there is no obvious best approach.</p>
<p>My approach was first to avoid adding a hamburger toggle to my site and instead just include the link names. I only have three links so there is really no reason to make the user click an extra button to find what they're looking for. On the home page, I also kept the introduction section relatively small with limited text before introducing my projects. After the projects I include a little 'about me' section with a little more info about me which really didn't need to come before the projects.</p>
<p>Try not to leave the user with a mountain of introductory/personal content to scroll through before getting to the projects.</p>
<h3 id="dont-include-the-portfolio-itself-as-a-project-if-you-have-enough-other-projects-to-show">Don't - Include the portfolio itself as a project if you have enough other projects to show</h3>
<p>This is something I deliberated at first and so I asked my friends over on Twitter what they thought about it. The general consensus was to avoid including the portfolio itself as one of the projects. This is especially true if you have plenty of other projects to highlight. Instead consider just adding a link to the Github repo somewhere. I like the idea of including it in the footer but it's up to you.</p>
<h3 id="do-if-youre-going-to-include-tutorial-projects-customize-them-in-some-way">Do - If you're going to include tutorial projects, customize them in some way</h3>
<p>It's fairly common to see projects on someone's portfolio that clearly came from a tutorial. We have all taken tutorials at some point and these projects can be a very useful learning curve. But if you want to add it to the portfolio, then customize it in some way that will make it a more personalized project.</p>
<p>For tutorial projects I would recommend not adding to the portfolio and instead just link your Github repo where the code lives. However if you're at the stage where you don't have many projects, it can be a temporary solution. Try changing the design of the tutorial project and some of your own features to it as well.</p>
<p>Personalize your projects to help you stand out!</p>
<h2 id="3-about-me">😃 3. About Me</h2>
<h3 id="dont-include-your-life-story">Don't - Include your life story</h3>
<p>I have seen plenty of portfolios that include a massive life story of the creator. It takes endless scrolling to get through before seeing the projects. I'm not saying you shouldn't include it, but I am saying you can probably reduce the size of it. Nobody needs to know what we had for breakfast or where we went to school 😄.</p>
<p>Give a few highlights instead that will quickly give a better picture of you as a person. Keep it short and concise or move it after the projects on the home page. You could also have a dedicated 'About me' page with more information there and have a quick snapshot of it on the homepage.</p>
<p>As long as you don't have a massive personal section ahead of your projects that require scrolling to get through it, you should be good!</p>
<h3 id="dont-include-skill-bars">Don't - Include skill bars</h3>
<p>This is a personal preference of mine that you may disagree with. You have probably seen those skill bars where the developer gives themselves a 9/10 for JavaScript as an example. In my opinion, this is not very useful information to be giving a potential employer. The employer will find out exactly how good you are when it comes to your interviews.</p>
<p>You could be overestimating your skills giving the employer the opportunity to question you on it which may trip you up. Or perhaps you're underestimating your skills and they deem you not to be worthy of the interview in the first place. </p>
<p>I just think that they don't provide any actual value to your portfolio. You can include the tech you are comfortable with but I wouldn't add a rating for what you think your skill level is. Use your projects to highlight your skills and what you could do better instead.</p>
<h2 id="4-tech-stack-doesnt-matter-performance-and-accessibility-does">✅ 4. Tech Stack doesn't matter, performance &amp; accessibility does</h2>
<h3 id="do-ensure-the-site-is-performant">Do - Ensure the site is performant</h3>
<p>The content and design are often the main focus for new developers when it comes to building the first portfolio. But good performance and accessible content are two incredibly important factors of any website that you need to get right. You could have all of the best projects in the world on your portfolio, but if it takes 5 seconds for the page to paint and render then what's the point?</p>
<p>No potential employer is going to consider you if they visit your site and it takes forever for it to load. They will think that you don't know how to optimize a site which will set off some red flags. At the end of the day, the stack you use to build the site is irrelevant. But it should always be performant and accessible. General tips for making it performant are as follows 👇</p>
<ul>
<li>Load the content you need for the visible screen and defer the rest when possible</li>
<li>Optimize any images you may have<ul>
<li>Use modern formats like WebP with a fallback option</li>
<li>Use a compressor tool like <a target="_blank" href="https://compressor.io/">Compressor.io</a></li>
<li>Use the correct image size for the device size</li>
<li>Lazy load images</li>
</ul>
</li>
<li>Bundle and minimize your files</li>
</ul>
<p>Use a tool like <a target="_blank" href="https://developers.google.com/web/tools/lighthouse">Lighthouse</a> to audit your site and give you potential improvements to make for performance as well as accessibility. </p>
<h3 id="do-ensure-your-site-is-accessible">Do - Ensure your site is accessible</h3>
<p>Make sure you use colors that contrast with each other so that users with deficiencies in viewing colors are catered for. You can use a tool like <a target="_blank" href="https://webaim.org/resources/contrastchecker/">WebAIM: Contrast Checker</a> to help you with this.</p>
<p>Make sure you always add alternative text to all of your images so that users using assistive technology can still understand your images. Also, try tabbing through your site as an impaired user may do. Ask yourself this 👇</p>
<blockquote>
<p>Is the tabbing order logical? </p>
</blockquote>
<p>and </p>
<blockquote>
<p>Can I still operate the site as any other user would?</p>
</blockquote>
<p>If the answer to these questions is no, you definitely want to correct this. Firefox has some great features in its developer tools for helping you view this information.</p>
<p>Navigate to the Accessibility tab in its developer tools and click the button 'Show Tabbing Order' to highlight all of the tabbable elements on the page. There is also a lot more information related to accessibility here like element roles so have a look around and see what improvements you can make.</p>
<h2 id="summary">Summary</h2>
<p>Let's quickly summarize the main points. Make your projects stand out as the highlight of the portfolio and ensure your contact information is easy to access. You can include information about yourself but don't make it detract from your projects. Finally, ensure the site is fast and accessible.</p>
<p>If you can manage all of this with some good projects on there as well, you're on the way to becoming a software developer. This is not to say that will be guaranteed to find a job. But it will help you get your foot in the door and perhaps receive more interview opportunities. It's then up to you to back up the stuff you claim to know from your portfolio.</p>
<p>I hoped this will help you in some way because making a portfolio can be a difficult thing to get right. Looking back at mine now, there are many things I would change and I'm sure I will be building a much-improved version sometime in the future for a bit of fun. But everything I have mentioned in the article stands true for me at least.</p>
<p>Reach out to me <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a> for any questions or concerns.</p>
<p>Until next time 👋</p>
]]></content:encoded></item><item><title><![CDATA[How the #100DaysOfCode challenge pushed me into landing my dream software development role]]></title><description><![CDATA[Last week I completed my #100DaysOfCode challenge over on Twitter. For those of you that don't already know, it is a commitment to be coding at least a little every day. You share it with others by tweeting about your activities and try to encourage ...]]></description><link>https://blog.kieranroberts.dev/how-the-100daysofcode-challenge-pushed-me-into-landing-my-dream-software-development-role</link><guid isPermaLink="true">https://blog.kieranroberts.dev/how-the-100daysofcode-challenge-pushed-me-into-landing-my-dream-software-development-role</guid><category><![CDATA[Web Development]]></category><category><![CDATA[motivation]]></category><category><![CDATA[General Advice]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Fri, 14 May 2021 07:59:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1620978980940/mBKHx6DLT.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last week I completed my #100DaysOfCode challenge over on Twitter. For those of you that don't already know, it is a commitment to be coding at least a little every day. You share it with others by tweeting about your activities and try to encourage others along the way.</p>
<p>Before I started the challenge I had no social media presence as a developer. No followers and no developer friends and I had been self-learning for about 10 months. Knowing that I would soon be looking for my first professional role, I thought it was important to have a social media presence and that it would significantly increase my chances of finding the right job.</p>
<p>But I didn't really know where to start or what I should be posting about? Then I found the #100DaysOfCode challenge where people of all stages in their learning were posting about what they were learning. This would be perfect I thought. I could post about my progress and meet others in the same position as me.</p>
<p>This challenge was absolutely the kick-start I needed and led to me meeting lots of amazing people I otherwise wouldn't have. This catalyst also led to me becoming a Full Stack Developer here at <strong>Hashnode</strong>.</p>
<p>Let me explain why this challenge was the catalyst for me getting to where I am today and why you might consider taking it on 👏.</p>
<h2 id="content">Content</h2>
<ol>
<li>📱 Provided content for my account</li>
<li>📈 Helped my social media growth</li>
<li>👩🏻‍🤝‍🧑🏽 Connected me with like-minded people</li>
<li>🙂 Kept me accountable</li>
<li>📝 It is a record of my progress</li>
<li>🤔 Is it for you?</li>
</ol>
<p>This was my first-day tweet 👇</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/Kieran6dev/status/1355204704230367232">https://twitter.com/Kieran6dev/status/1355204704230367232</a></div>
<h3 id="1-provided-content-for-my-account">📱 1. Provided content for my account</h3>
<p>Having a good social media presence as a developer can be hugely beneficial. If you're like me and start the challenge with no presence at all it might be difficult to know what you want to post about and where to start. </p>
<p>I didn't feel comfortable posting in my first few weeks but this challenge forced me to keep doing it. The more you do something, the easier it gets. Similar to when I started blogging, thinking of suitable content is difficult and so a great way of overcoming this is to write about yourself.</p>
<p>The challenge requires you to post a tweet each day about what you've been learning. It pushes you to write about yourself and I continued this trend into my articles. By tweeting out your progress you start to build a collection of tweets that may be important to have if people are going to feel as though they want to follow you. People are usually less likely to follow a person if they have none of their own tweets on their timeline because it tells them they are probably less likely to engage in general.</p>
<p>The 100-day coding challenge gave me a starting point to begin adding value to my feed which I probably would have been stuck on otherwise. I found it refreshing to be able to share with others the stuff that went well, as well as the stuff that didn't. It meant I would have at least one post on my feed every single day. If you commit all the way to the end of the challenge you will have more than three months' worth of consistent tweets just from the challenge alone!</p>
<p>It will help get you off the ground and into the habit of posting consistently. You will need this consistency if you aim to build a social media presence and use it to help you search for employment or freelancing opportunities.</p>
<h3 id="2-helped-my-social-media-growth">📈 2. Helped my social media growth</h3>
<p>When I started the challenge I think I had less than 5 followers and I didn't even have a Linkedin account. If your aim is to grow your social media presence to help you find opportunities I think we can both agree that this will not work out.</p>
<p>I hadn't posted a single tweet on my account before this challenge. Most of it was down to anxiety and putting myself out there. I would say I'm an introvert at heart who has learned to become socially extroverted when required. It's just a fact that I was afraid of posting 😥.</p>
<p>Knowing that I was going to be applying for my first full-time role in the near future, I wanted to change this but I didn't really know how to do it. I would see some popular accounts tweeting tips and think I had come up with a super-secret amazing tip every time that has never been seen before. That's not the case of course and I soon found my way that started with what I was learning.</p>
<p>I soon connected with lots of other developers also taking part in the challenge. This gave me the confidence to start posting some of my own content including starting to write a blog which was supported by lots of the friends I have made. It's like the snowball effect where it keeps getting bigger as it rolls ⛄.</p>
<p>Before you know it I became comfortable with the content I was providing and started sharing things about myself as well that I never would have considered before. Showing others you are a person and not just an account will go a long way to helping you connect with others. Today I'm proud to say I have made a lot of awesome friends and I never would have thought I'd be ending the challenge with close to 1k meaningful connections. Many of which I communicate with regularly.</p>
<p>#100DaysOfCode is a very popular hashtag that will be seen by many people and can help extend the reach of your progress tweets early on when it's possible you don't have many connections. If you combine this with adding value to other' tweets you are sure to see some growth and start making connections that could help your future in one way or another.</p>
<h3 id="3-connected-me-with-like-minded-people">👩🏻‍🤝‍🧑🏽 3. Connected me with like-minded people</h3>
<p>This is great for a multitude of reasons. Probably the best part of the challenge was meeting lots of like-minded people who were going through the same struggles as me. For the previous year I had been working alone without anyone in the same field to really share things with.</p>
<p>I soon met lots of awesome people through the challenge who were openly sharing and encouraging each other. It was awesome to see what everyone was building from week to week. You can clearly see how much progress your friends are making and this should help drive you on to do the same.</p>
<p>The absolute biggest benefit for me so far in my progress as a developer has come from connecting with others. There is always something to learn from another developer whether they're behind you in the process or far more experienced than you. <strong>Everyone has something to share</strong>. This is the phrase I used to push myself to start writing.</p>
<p>This openness to share and help others was new to me and I loved it. Not only will you learn so much from each other, but you might connect with people who can help you look for opportunities whether it's direct or through sharing that you're looking for a job. You might be surprised to hear how many job and freelance opportunities can be found on places like Twitter.</p>
<h3 id="4-kept-me-accountable">🙂 4. Kept me accountable</h3>
<p>Staying consistent is <strong>the most important factor</strong> when learning to write code. It will help you if you can include others in your attempts to be consistent. This usually comes from sharing with others like the opportunity provided by this challenge.</p>
<p>Posting your progress every day will open you up to others and suddenly your not the only one interested in how you're getting on. I never felt pressured to continue in any way. I just wanted to. If you want to grow your social media presence and learn to code, you're going to want to be consistent or it's going to take you twice as long to get where you want to be.</p>
<p>I made a lot of progress on the coding side of things during this challenge. Partly down to the push given by the challenge and it culminated in me finding my first role in the industry.</p>
<h3 id="5-it-is-a-record-of-my-progress">📝 5. It is a record of my progress</h3>
<p>Making notes and keeping track of your development is a great way of seeing how far you've come and where you might want to go in the future. Every day you post a tweet talking about what you worked on for the day like you would if you were writing a diary. The good and the bad.</p>
<p>Having recently completed the challenge, it was interesting for me to look back through some of the days to see what I accomplished and also the things I struggled with. Below is a quick list of some of the things I worked on during the challenge.</p>
<ul>
<li>Built a full stack next.js serverless e-commerce website</li>
<li>Designed and built a modern landing page</li>
<li>Started my blog</li>
<li>Worked on many different topics like 👇<ul>
<li>Git</li>
<li>React</li>
<li>Data structures</li>
<li>TypeScript</li>
<li>Design with Figma</li>
<li>Testing</li>
<li>Several different styling libraries/frameworks</li>
</ul>
</li>
</ul>
<p>and so much more. Some of this I might not have started with. Or perhaps it might not have gone as well as it did without the push from the challenge. Things like my landing page design/build project and this blog your reading right now were inspired by the people I met and the quality of work they were producing. </p>
<h3 id="6-is-it-for-you">🤔 6. Is it for you?</h3>
<p>I would definitely recommend you to take on the challenge if you fall into one of two categories. Maybe like me, you want to join communities of other developers but you're not sure where to start or what content you want to be posting about. Or perhaps you're still new to web development and you want to challenge yourself and learn to code by staying consistent. If you fall into a different category and would like to take on the challenge, then by all means go ahead. Only you will know whether it's right for you.</p>
<p>Whatever it is there are obvious benefits to it which is why it is a very popular challenge over on Twitter. I can't say for sure that I would have put myself out there like I have since without it and I could have missed out on all the awesome things that have happened since.</p>
<p>Starting the challenge is the moment I can pinpoint as the catalyst for a lot of the amazing opportunities I've experienced since. This doesn't mean that starting the challenge is guaranteed to lead to great things. It's up to you to put in the hard work yourself. What it did do was open my eyes and push me to where I am today and I am very grateful for this.</p>
<h3 id="conclusion">Conclusion</h3>
<p>Things would be different now for me if I hadn't started the challenge when I did. If you're currently taking on the challenge then let me about it!</p>
<p>If you would like to connect with me then you can find me <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a>. Feel free to send me a message anytime if you think I can help you with something.</p>
<p>Want another read 👉 <a target="_blank" href="https://blog.kieranroberts.dev/i-took-part-in-5amcoders-these-are-the-benefits-i-saw-from-being-productive-earlier-in-the-day">I took part in #5amCoders! These are the benefits I saw from being productive earlier in the day</a></p>
<p><a href="https://www.buymeacoffee.com/kieran6roberts"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a beer&amp;emoji=🍺&amp;slug=kieran6roberts&amp;button_colour=FFDD00&amp;font_colour=000000&amp;font_family=Cookie&amp;outline_colour=000000&amp;coffee_colour=ffffff" /></a></p>
<p>See you soon 👋</p>
]]></content:encoded></item><item><title><![CDATA[Getting started with Next.js API routes]]></title><description><![CDATA[Next.js is a wonderful production-ready framework built on the JavaScript library React. It's extremely flexible letting us build simple static sites, server-side rendered apps, or a mix of both. One of the cool features of Next.js is how it handles ...]]></description><link>https://blog.kieranroberts.dev/getting-started-with-nextjs-api-routes</link><guid isPermaLink="true">https://blog.kieranroberts.dev/getting-started-with-nextjs-api-routes</guid><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Fri, 07 May 2021 11:59:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1620319132806/sZl4mET3h.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Next.js is a wonderful production-ready framework built on the JavaScript library React. It's extremely flexible letting us build simple static sites, server-side rendered apps, or a mix of both. One of the cool features of Next.js is how it handles the routing system.</p>
<p>Routes can simply be created by adding a file into the <code>/pages</code> folder that is created for you when you bootstrap an app with <code>create-next-app</code>. Inside of here there is another folder called <code>api</code>. When you create a file inside of <code>api</code> it will be accessible at the route <code>/api/...</code>. But you won't be served with a page this time.</p>
<p>This is an <strong>API endpoint</strong> belonging to the server-side of your app and is intended to return data, not pages. Here we can write backend code using Node.js if we desire. You might be a bit hesitant about this especially if you come from the position of a front-end developer. This transition between one minute writing Node.js code on the server and the next minute writing react code in the client is a feature of Next.js. and it's awesome when you get going.</p>
<p>I'm here to introduce you to these API routes. Let's go 👏.</p>
<h2 id="heading-content">Content</h2>
<ol>
<li><p>Why should we use the <code>/api</code> routes</p>
</li>
<li><p>Our first API route</p>
</li>
<li><p>Request and Response</p>
</li>
<li><p>Dynamic Routes</p>
</li>
<li><p>What else might we use an API route for?</p>
</li>
<li><p>Typing API routes with TypeScript</p>
</li>
<li><p>API route configuration</p>
</li>
</ol>
<p>You can check out the Next.js documentation yourself here 👉 <a target="_blank" href="https://nextjs.org/">Next.js</a></p>
<h3 id="heading-1-why-should-we-use-the-api-routes">1. Why should we use the <code>/api</code> routes</h3>
<p>A nice way of thinking about a Next.js API route is as a middle-man for your app. Time for an analogy! 😃</p>
<p>Consider a person called John who wants to send a letter to his friend Jane. Once Jane receives the letter she will send a reply back to John. But how do the letters get from one place to another? The Postman/Postwoman of course!</p>
<p>The Postman/Postwoman acts as the middleman. This person handles the logic of transporting the letters where they need to be and this is kind of what we do with Next.js API routes. Often we make a request to an API route with some data. This is like posting your mail into the letterbox.</p>
<p>Then behind the scenes, the post will go through things like sorting and manipulation before being sent to the other end (like an external API). When the person receives and replies to the letter, eventually the original sender will open the reply (response).</p>
<p>So if our API routes are sometimes also sending requests, you might ask something like this 👇</p>
<blockquote>
<p>Why should I make a request to <code>/api/posts</code> and then from there make another request?</p>
</blockquote>
<p>There are a few reasons why you might want to do this.</p>
<ul>
<li><p>You can access your environment variables safely with <code>process.env</code> on the server-side</p>
</li>
<li><p>You can hide your interaction with some external provider by keeping it out of the client</p>
</li>
<li><p>Maybe you have cookies you want to handle on the server</p>
</li>
</ul>
<p>Instead of making a request to <code>https://externalsite.com</code> from the client we instead make the request to our API route <code>api/...</code> with a body of data if required. These routes can act as the go-between for our app to safely communicate between our client and our serverless requirements.</p>
<p>It should be noted that access to Next.js API routes is only accessible from the same origin unless configured otherwise with middleware such as CORS.</p>
<h3 id="heading-2-our-first-api-route">2. Our first API route</h3>
<p>I'll start by setting up our project directory called <code>next-api-routes</code> with Next.js using the following command</p>
<pre><code class="lang-plaintext">npx create-next-app next-api-routes
</code></pre>
<p>Our first example will be a simple request for some data related to blog posts. I will hardcode the data inside a JSON file. Create a file called <code>posts.json</code> and place it at the root of your project alongside <code>package.json</code> and the rest. My data looks like this 👇</p>
<pre><code class="lang-json"><span class="hljs-comment">// posts.json</span>
[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"1"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Ditch the dreaded &lt;div /&gt;. Semantic HTML elements we should use instead"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"2"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Monday motivation - How I got into code"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"3"</span>,
    <span class="hljs-attr">"title"</span>: <span class="hljs-string">"How to setup path aliases for development with Next.js &amp; Jest"</span>
  }
]
</code></pre>
<p>Now create a file called <code>posts</code> in the <code>api</code> folder so we can hit this route with a request to <code>/api/posts</code>. You can delete the current <code>hello.js</code> file or remove its content and rename it. Inside the <code>/api/posts</code> file we have the following code 👇.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// pages/api/posts.js</span>
<span class="hljs-keyword">import</span> posts <span class="hljs-keyword">from</span> <span class="hljs-string">"../../posts.json"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> (req, res) =&gt; {
  res.status(<span class="hljs-number">200</span>).json(posts);
}
</code></pre>
<p>Essentially our API routes are just functions. We have one default exported function per API route. Here we are also importing our hardcoded posts. The function receives two parameters <code>req</code> and <code>res</code> which are defined as the following.</p>
<ul>
<li><p><code>req</code> - The request object based on the incoming HTTP message</p>
</li>
<li><p><code>res</code> - The response object based on the HTTP response of the server</p>
</li>
</ul>
<p>We will explore the <code>req</code> and <code>res</code> objects in detail in the next section. You'll be familiar this these if you've worked with Node.js and Express before. Here we send back a response code of 200 indicating it was successful and we can chain this status with <code>.json()</code> response method to send back a JSON response of our posts. Since our list is hardcoded and imported we know it is going to be successful hence the 200 status code.</p>
<p>You can navigate in the browser to the route <code>/api/posts</code> and you should see a response like this 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620291530711/XgHrTxcwZ.jpeg" alt="JSON response of our hardcode posts" /></p>
<p>and this is in JSON format. Of course this is just data, not a page. To use the data in our app we would need to send a request for it.</p>
<p>Now we have the route setup we are going to hit it from our app and access the data. We'll work out of the <code>pages/index.js</code> file for the purpose of these examples. The cool thing about Next.js is that we can choose exactly when we need access to this data. We can fetch in on the server using <code>getServerSideProps()</code> or <code>getStaticProps()</code>. We can alternatively fetch in on the client after the page loads. It depends on what the data is and what you want to do with it.</p>
<p>I'll show examples of both because it's very similar. Again you can remove the default markup of this file inside the return method.</p>
<p>First up we'll fetch it client-side on a button click like this 👇</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// pages/index.js</span>
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

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

  <span class="hljs-keyword">const</span> [ posts, setPosts ] = React.useState([]);

  <span class="hljs-keyword">const</span> handleFetchPosts = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">const</span> postsResponse = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/posts"</span>);
      <span class="hljs-keyword">const</span> postsData = <span class="hljs-keyword">await</span> postsResponse.json();
      setPosts(postsData);
  };

  <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">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleFetchPosts}</span>&gt;</span>Fetch Posts<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      {posts.map(({ id, title }) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>&gt;</span>{title}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}
</code></pre>
<p>Or you could pre-render the page server-side like this 👇</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// pages/index.js</span>
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">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">{ posts }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      {posts.map(({ id, title }) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>&gt;</span>{title}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getServerSideProps</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> postsResponse = <span class="hljs-keyword">await</span> fetch(process.env.BASE_URL + <span class="hljs-string">'/api/posts'</span>);
  <span class="hljs-keyword">const</span> postsData = <span class="hljs-keyword">await</span> postsResponse.json();

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">props</span>: {
      <span class="hljs-attr">posts</span>: postsData
    }
  }
}
</code></pre>
<p>These examples don't include error handling due to the fact we know the data exists. A few things to note in the server-side example. Here if you pass the same URL to the fetch function as the previous example you'll see this error 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620111274635/qgxB7KBKq.jpeg" alt="getServerSideProps only absolute URL's are supported error" /></p>
<p>To fix this we need to change it to an absolute path. Because we're on the server-side we can access our environment variables using <code>proccess.env</code> and I create a variable called <code>BASE_URL</code>. This variable holds the value <code>http://localhost:3000</code> for now. If you were building an application to be deployed you would want this to be configurable based on the environment.</p>
<p>Once we have the data we return it through the <code>props</code> object and access it through the <code>props</code> of our home page. This time I removed the button and instead decide to pre-render the page with our data.</p>
<p>Checkout the Next.js documentation on <code>getServerSideProps()</code> if you need more information <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering">Next.js - <code>getServerSideProps</code> (Server-Side Rendering)</a></p>
<h3 id="heading-3-request-and-response">3. Request and Response</h3>
<p>Next.js provides us with middleware built into API routes to parse the request.</p>
<ul>
<li><p><code>req.body</code> - The request body where we send some data to our route</p>
</li>
<li><p><code>req.cookies</code> - Cookie object sent with the request</p>
</li>
<li><p><code>req.query</code> - URL query string object of the request</p>
</li>
</ul>
<p>Let's take a look at some of the things we can access from the request. The following code is in our <code>pages/index.js</code> file and it will send a request to a route called <code>user.js</code> at <code>/api/user</code> with some data.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// pages/index.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">const</span> handleSubmission = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> info = {
      <span class="hljs-attr">name</span>: <span class="hljs-string">"Kieran"</span>,
      <span class="hljs-attr">age</span>: <span class="hljs-number">26</span>
    }

    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"/api/user"</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>
      },
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(info)
    });

    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json();
    <span class="hljs-built_in">console</span>.log(data) <span class="hljs-comment">// { message: "success" }</span>
  }

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleSubmission}</span>&gt;</span>
        Hit Route
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>All we are doing here is sending our request with our <code>info</code> object in the request body in JSON format. To do this we send a <code>POST</code> request with our data when we click the button.</p>
<p>Next up let's see what data we can access on the request. I have added a new API route at <code>/api/user.js</code> with the following code 👇</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// pages/api/user.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">user</span>(<span class="hljs-params">req, res</span>) </span>{
    <span class="hljs-built_in">console</span>.log(req.body); <span class="hljs-comment">// {"name":"Kieran","age":26}</span>
    <span class="hljs-built_in">console</span>.log(req.query) <span class="hljs-comment">// {} in our example</span>

    <span class="hljs-built_in">console</span>.log(req.method); <span class="hljs-comment">// POST</span>
    <span class="hljs-built_in">console</span>.log(req.headers.host); <span class="hljs-comment">// localhost:3000</span>
    <span class="hljs-built_in">console</span>.log(req.url); <span class="hljs-comment">// /api/user</span>

    res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"success"</span> })
}
</code></pre>
<p>Don't forget to send a response back from your API routes like we do here with <code>res.json()</code>, otherwise you'll see this 👇</p>
<blockquote>
<p>API resolved without sending a response for /api/user, this may result in stalled requests.</p>
</blockquote>
<p>Above you'll see some of the things we can access on the request and the result of each in our example. If you log the request object to the console you will see a large amount of data returned. Don't forget that because this is running on the server, you won't be able to see this logged in the browser. You'll find it in your terminal instead.</p>
<p>Now let's take a look at the response. As we've previously explored, our API routes often act like middleware for our app between our client and external services. The general flow is like this 👇</p>
<ol>
<li><p>Send a request to the API route</p>
</li>
<li><p>Do something with the request</p>
</li>
<li><p>Finish by returning a response</p>
</li>
</ol>
<p>If you're familiar with Node.js and Express then the following will be familiar to you. Next.js provides methods on the <code>res</code> object that let us control how we return a response. Let's check them out 👇</p>
<ol>
<li><code>res.status(statusCode)</code> - Return a status code in the response headers</li>
</ol>
<ul>
<li><p>200's- Success</p>
</li>
<li><p>300's - Redirect</p>
</li>
<li><p>400's - Client errors</p>
</li>
<li><p>500's - Server errors</p>
</li>
</ul>
<ol start="2">
<li><p><code>res.json(json)</code> - Return a JSON response object</p>
</li>
<li><p><code>res.redirect([ status ], path)</code> - Return a redirect with a status code</p>
</li>
<li><p><code>res.send(body)</code> - Return the response body as a an object, string or buffer</p>
</li>
</ol>
<p>As you've seen in previous examples we can chain these together to return a status code with some data such as <code>res.status(500).json({ message: "Bad request" });</code> allowing us to better control our responses.</p>
<h3 id="heading-4-dynamic-routes">4. Dynamic Routes</h3>
<p>In Next.js we can also create dynamic routes that match different queries.</p>
<p>Using our posts example from earlier, let's see an example of dynamic API routes. Create a directory called <code>posts</code> inside the <code>api</code> folder. Then create a file called <code>[postId].js</code> inside the <code>posts</code> directory.</p>
<p>We use the square bracket syntax for dynamic routes and pass in a parameter name of your choice You should end up with <code>/api/posts/[postId].js</code>. We have the following code inside this file 👇.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> posts <span class="hljs-keyword">from</span> <span class="hljs-string">"../../../posts.json"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> (req, res) =&gt; {
    <span class="hljs-keyword">const</span> post = posts.find(<span class="hljs-function">(<span class="hljs-params">{ id }</span>) =&gt;</span> id === req.query.postId);

    <span class="hljs-keyword">if</span> (post) {
        res.status(<span class="hljs-number">200</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"success"</span>, post });
    } <span class="hljs-keyword">else</span> {
        res.status(<span class="hljs-number">400</span>).json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"post not found"</span> });
    }
}
</code></pre>
<p>You can test this out in the browser by navigating to <code>http://localhost:3000/api/posts/1</code>. We can access the query using the middleware <code>req.query</code> as we've previously seen. Then we can use this to see if the article exists in our hardcoded posts based on the id we pass to the query.</p>
<p>If it is present, we send back a success response with the post. Otherwise we send back a not found status with a generic error message.</p>
<p>Here you can see the successful response in the browser 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620374426812/c0dlHt_pW.jpeg" alt="Success response with post and success message in browser" /></p>
<p>and the fallback response 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1620374461572/HeDDHe0ZG.jpeg" alt="Failed response with a generic error message in browser" /></p>
<h3 id="heading-5-what-else-might-we-use-an-api-route-for">5. What else might we use an API route for?</h3>
<p>I wanted to share some real examples where you might use Next.js API routes. Recently I built a full-stack serverless e-commerce site using Next.js and these routes allowed me to create a dynamic site using these serverless functions.</p>
<p>I have an article focused on that site here - <a target="_blank" href="https://blog.kieranroberts.dev/i-built-a-full-stack-serverless-e-commerce-site-with-nextjs-heres-an-honest-review-of-what-i-learned-and-how-it-might-help-you">I built a full-stack serverless e-commerce site with Next.js. Here's an honest review of what I learned and how it might help you</a></p>
<p>The API routes served as the middle-man for my app's authentication, in this case using <a target="_blank" href="https://auth0.com/">auth0</a>. I created routes for handling login, logout, session, and more all without the need of a server.</p>
<p>I also have serverless functions for creating a checkout session using the payment platform Stripe and handling the creation of orders based on successful payments. The possibilities are endless!</p>
<h3 id="heading-6-typing-api-routes-with-typescript">6. Typing API routes with TypeScript</h3>
<p>Recently I have been learning TypeScript so I wanted to quickly share an example of how you can type the request and response of an API route function. Using our previous posts example we can type it like this 👇</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// pages/api/posts/[postId].js</span>
<span class="hljs-keyword">import</span> posts <span class="hljs-keyword">from</span> <span class="hljs-string">"../../../posts.json"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { NextApiRequest, NextApiResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;

<span class="hljs-keyword">type</span> ResponseSuccessMessage = <span class="hljs-string">"success"</span>;
<span class="hljs-keyword">type</span> ResponseFallbackMessage = <span class="hljs-string">"post not found"</span>;
<span class="hljs-keyword">interface</span> Post {
    id: <span class="hljs-built_in">string</span>;
    title: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">interface</span> PostSuccessResponse {
    message: ResponseSuccessMessage;
    post: Post;
}

<span class="hljs-keyword">interface</span> PostFallbackResponse {
    message: ResponseFallbackMessage;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> (req: NextApiRequest, res: NextApiResponse&lt;PostSuccessResponse | PostFallbackResponse&gt;) =&gt; {
    <span class="hljs-keyword">const</span> post = posts.find(<span class="hljs-function">(<span class="hljs-params">{ id }</span>) =&gt;</span> id === req.query.postId);

    <span class="hljs-keyword">if</span> (post) {
        res.status(<span class="hljs-number">200</span>).json({ message: <span class="hljs-string">"success"</span>, post });
    } <span class="hljs-keyword">else</span> {
        res.status(<span class="hljs-number">400</span>).json({ message: <span class="hljs-string">"post not found"</span> });
    }
}
</code></pre>
<h3 id="heading-7-api-route-configuration">7. API route configuration</h3>
<p>Next.js also allows us to customize the configuration of the route in a couple of different ways. We do this by exporting a <code>config</code> object from the route like this 👇.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> posts <span class="hljs-keyword">from</span> <span class="hljs-string">"../../posts.json"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> (req: NextApiRequest, res: NextApiResponse) =&gt; {
  res.status(<span class="hljs-number">200</span>).json(posts);
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
  api: {
    bodyParser: <span class="hljs-literal">false</span>, <span class="hljs-comment">// set to true by default which parses request bodies</span>
    externalResolver: <span class="hljs-literal">true</span> <span class="hljs-comment">// sets whether the route is handled by something like Express </span>
  }
}
</code></pre>
<p>In most cases, this won't be necessary but it's useful to know it's there. One thing to note is that the default config is as follows 👇</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
  api: {
    bodyParser: {
      sizeLimit: <span class="hljs-string">"1mb"</span>,
    }
  }
}
</code></pre>
<p>This means <code>bodyParser</code> is enabled by default meaning that even if you don't include the <code>Content-Type</code> header in your request, the request body will still be parsed using the default config. If you set <code>bodyParser</code> to false you have to make sure to send the correct headers but I recommend leaving this set to true.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>That is my introduction to Next.js API routes. I hope you enjoyed and if you did let me know about it here on Hashnode or <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a> where I'm always available.</p>
<p>I would love to write an article in the future where we build a small app using Next.js API routes including something like authentication or payments. Let me know if that interests you!</p>
<p><a target="_blank" href="https://www.buymeacoffee.com/kieran6roberts">![](https://img.buymeacoffee.com/button-api/?text=Buy me a beer&amp;emoji=🍺&amp;slug=kieran6roberts&amp;button_colour=FFDD00&amp;font_colour=000000&amp;font_family=Cookie&amp;outline_colour=000000&amp;coffee_colour=ffffff align="left")</a></p>
<p>Until next time 👋</p>
]]></content:encoded></item><item><title><![CDATA[Ditch the dreaded <div />. Semantic HTML elements we should use instead]]></title><description><![CDATA[Firstly we'll clear up the meaning of semantic HTML. The semantics of an entity describes what its purpose is. By using semantic HTML elements we are able to provide meaning to the structure of our code. If you think about the <div> tag for a second ...]]></description><link>https://blog.kieranroberts.dev/ditch-the-dreaded-lessdiv-greater-semantic-html-elements-we-should-use-instead</link><guid isPermaLink="true">https://blog.kieranroberts.dev/ditch-the-dreaded-lessdiv-greater-semantic-html-elements-we-should-use-instead</guid><category><![CDATA[HTML5]]></category><category><![CDATA[HTML]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Accessibility]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Thu, 29 Apr 2021 14:20:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619769213858/7eIIsSQcS.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Firstly we'll clear up the meaning of semantic HTML. The semantics of an entity describes what its purpose is. By using semantic HTML elements we are able to provide meaning to the structure of our code. If you think about the <code>&lt;div&gt;</code> tag for a second ask yourself this: If you had never heard of this element before what would you think its purpose was? </p>
<p>Difficult right? 🤔</p>
<p>Imagine trying to build a new desk from one of those sets you can buy at a furniture store. It includes everything you need to construct it as well as an informational booklet with all the information related to the desk. </p>
<p>Now imagine each page of the instruction booklet is missing a header. It makes finding the instruction page much more difficult as well as the list of contents in the box, the warranty policy, and so on. You can still find them but you have to look very closely. </p>
<p>The name <code>&lt;div&gt;</code> does not offer any information as to what role it might perform. Now think of the <code>&lt;h1&gt;</code> tag or any heading tag for that matter. The heading element has a role that describes the role it performs (as a heading). There should only ever be <em>one</em> <code>&lt;h1&gt;</code> element on the page.</p>
<p>There are three primary advantages to using semantic HTML elements to you and the users of your site:</p>
<ul>
<li><strong>Search Engine Optimization</strong></li>
</ul>
<p>This refers to the way in which search engines such as Google interpret the content of your site and it can affect where your site will appear in search results. This means that neglecting semantic HTML could have a negative effect on how many users will find and interact with the site.</p>
<ul>
<li><strong>Accessibility</strong></li>
</ul>
<p>Using elements that better describe the role they perform makes it easier for screen readers to inform users with disabilities about the content of your site.</p>
<ul>
<li><strong>Code readability and maintainability</strong></li>
</ul>
<p>Using semantic HTML elements can help you with the maintaining and debugging of your code making it clear which part of the code you are looking at. This is especially important if you are working with other developers in the same codebase.</p>
<p>There are many semantic elements we can use that in principle do the same thing but offer the benefits outlined above. Now that we understand what a semantic element is and why they are important, let me show you some semantic elements that we can use to replace the <code>&lt;div&gt;</code> that you might still be using a little too often.   <br />  </p>
<p>Throughout the article I will include quote definitions for each element using <a target="_blank" href="https://developer.mozilla.org/en-US/">MDN Web Docs</a> before explaining them in simpler terms.</p>
<p>Let's begin 👏.</p>
<h2 id="semantic-elements">Semantic Elements</h2>
<ol>
<li><code>&lt;article&gt;</code></li>
<li><code>&lt;header&gt;</code></li>
<li><code>&lt;section&gt;</code></li>
<li><code>&lt;nav&gt;</code></li>
<li><code>&lt;main&gt;</code></li>
<li><code>&lt;aside&gt;</code></li>
<li><code>&lt;details&gt;</code> &amp; <code>&lt;summary&gt;</code></li>
<li><code>&lt;figure&gt;</code> &amp; <code>&lt;figcaption&gt;</code></li>
<li><code>&lt;blockquote&gt;</code></li>
<li><code>&lt;abbr&gt;</code></li>
<li><code>&lt;footer&gt;</code></li>
</ol>
<h3 id="1-lessarticlegreater">1. <code>&lt;article&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML <code>&lt;article&gt;</code> element represents a self-contained composition in a document, page, application, or site, which is intended to be independently distributable or reusable (e.g., in syndication). Examples include: a forum post, a magazine or newspaper article, or a blog entry, a product card, a user-submitted comment, an interactive widget or gadget, or any other independent item of content.</em></p>
</blockquote>
<p>This element is often used to wrap pieces of content where your codebase might contain multiples of them. An example of this is a blog post preview card or the post itself but really you could use it whenever you have a piece of content that includes a heading to describe it and the content representative of the heading. You can also nest <code>&lt;article&gt;</code> elements inside each other.<br /></p>
<p>Example 👇</p>
<pre><code>&lt;article <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">product</span>"&gt;
  &lt;h2&gt;
    <span class="hljs-title">Coffee</span>
  &lt;/h2&gt;
  &lt;article class="product-info&gt;
    &lt;h3&gt;
      <span class="hljs-title">Product</span> <span class="hljs-title">information</span>
    &lt;/h3&gt;
    &lt;p&gt;
      <span class="hljs-title">Very</span> <span class="hljs-title">delicious</span> <span class="hljs-title">coffee</span>!
    &lt;/p&gt;
  &lt;/article&gt;
&lt;/article&gt;</span>
</code></pre><h3 id="2-lessheadergreater">2. <code>&lt;header&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML <code>&lt;header&gt;</code> element represents introductory content, typically a group of introductory or navigational aids. It may contain some heading elements but also a logo, a search form, an author name, and other elements.</em></p>
</blockquote>
<p><br />This element is typically useful for grouping elements that represent the header of an article or post together where at least one of the elements includes a heading element such as <code>&lt;h2&gt;</code>. I have also used it in the hero of a landing page of a site that includes the main heading element <code>&lt;h1&gt;</code> and possibly some images, text, links or other introductory content that support the heading.</p>
<p>Although called a header element it is not necessary to use this element at the top of the page and can used in other sectional elements such as <code>&lt;article&gt;</code> and <code>&lt;section&gt;</code>.<br /></p>
<p>Examples 👇</p>
<pre><code>&lt;header <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"header"</span>&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
    Hero header
  <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"heroimage.webp"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"a nice descriptive alt text"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span></span>
</code></pre><p>or </p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>
      Article header
    <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/blog-article"</span>&gt;</span>
      Link
    <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
  ...
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
</code></pre><h3 id="3-lesssectiongreater">3. <code>&lt;section&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML <code>&lt;section&gt;</code> element represents a generic standalone section of a document, which doesn't have a more specific semantic element to represent it. Sections should always have a heading, with very few exceptions.</em></p>
</blockquote>
<p>Similar to the <code>&lt;div&gt;</code> there is often a better choice than the <code>&lt;section&gt;</code> element. Even so it is an improvement on the <code>&lt;div&gt;</code> when you need to section-off a piece of content that would not be suitable for a <code>&lt;nav&gt;</code> or <code>&lt;article&gt;</code> or similar elements (as long as it is not used for styling purposes). </p>
<p>If you require an element wrapper for styling purposes it is better to stick to the <code>&lt;div&gt;</code> element. <br /></p>
<p>Example 👇</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"blog-articles"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>
    Section header
  <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
  ...any other content related to the section
<span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
</code></pre><h3 id="4-lessnavgreater">4. <code>&lt;nav&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML <code>&lt;nav&gt;</code> element represents a section of a page whose purpose is to provide navigation links, either within the current document or to other documents. Common examples of navigation sections are menus, tables of contents, and indexes.</em></p>
</blockquote>
<p><br />Use it to wrap the primary sets of links that are used to navigate around your page indicating clearly to your screen reader users exactly where the main navigational links of your site are based. <br /></p>
<p>Example 👇</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"main-nav"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</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">li</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/blog"</span>&gt;</span>
        Blog
      <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
</code></pre><h3 id="5-lessmaingreater">5. <code>&lt;main&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML <code>&lt;main&gt;</code> element represents the dominant content of the <code>&lt;body&gt;</code> of a document. The main content area consists of content that is directly related to or expands upon the central topic of a document, or the central functionality of an application.</em></p>
</blockquote>
<p>You will only ever have one <code>&lt;main&gt;</code> element per page of a site that indicates to the user where the primary content of the page exists. This is the content that is left when you ignore secondary content that is repeated across other pages of the site such as the content enclosed in the <code>&lt;nav&gt;</code>, <code>&lt;footer&gt;</code>, <code>&lt;aside&gt;</code> and similar additional elements. These are sometimes called layout components/elements.</p>
<p>It also has an important role for users requiring a screen reader to navigate your site because they are able to instruct their browser to skip straight to the <code>&lt;main&gt;</code> element and the content it includes. <br /></p>
<p>Example 👇</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"main-nav"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    ...navigational links
  <span class="hljs-tag">&lt;/<span class="hljs-name">ul</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">header</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"landing"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
    Some descriptive page header
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    ...explanation
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/blog"</span>&gt;</span>
    call to action for user
  <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>
    Welcome to the main content of the page
  <span class="hljs-tag">&lt;/<span class="hljs-name">h2</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">h3</span>&gt;</span>
      Article title
    <span class="hljs-tag">&lt;/<span class="hljs-name">h3</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">main</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
  ...
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span></span>
</code></pre><h3 id="6-lessasidegreater">6. <code>&lt;aside&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML <code>&lt;aside&gt;</code> element represents a portion of a document whose content is only indirectly related to the document's main content. Asides are frequently presented as sidebars or call-out boxes.</em></p>
</blockquote>
<p>The <code>&lt;aside&gt;</code> element is to provide some content that is secondary to whatever content it relates to. This means that wherever the element is in the document, it should only contain content that the user does not explicitly require to understand the primary content.</p>
<p>An example is in an article to provide a definition for something or a further explanation the relates to the content. Maybe some links to other articles that expand on a definition further. <br /></p>
<p>Example 👇</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>
    Article header
  <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    I really enjoy writing JavaScript.
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">aside</span>&gt;</span>
    JavaScript is a high-level dynamically typed programming language. You can find more information here ...
  <span class="hljs-tag">&lt;/<span class="hljs-name">aside</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    I also really like semantic HTML.
  <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>
</code></pre><h3 id="7-lessdetailsgreater-and-lesssummarygreater">7. <code>&lt;details&gt;</code> &amp; <code>&lt;summary&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML Details Element (<code>&lt;details&gt;</code>) creates a disclosure widget in which information is visible only when the widget is toggled into an "open" state. A summary or label can be provided using the <code>&lt;summary&gt;</code> element.</em></p>
</blockquote>
<p>These two elements are really useful when you require a widget that can be toggled to an 'open' or 'close' state that reveals or hides some content. A typical use case for this is an faq (frequently asked questions) section of a website that allows the user to click on a question to reveal the answer.</p>
<p>It uses no JavaScript meaning it will continue to function properly for your uses even if they have set their browser to ignore it, making your site more accessible. The <code>&lt;summary&gt;</code> tag contains the text that is always visible to the user while the <code>&lt;details&gt;</code> tag holds the information to display when the widget is in the 'open' state. <br /></p>
<p>Example 👇</p>
<pre><code>&lt;details&gt;
  &lt;<span class="hljs-keyword">summary</span>&gt;
    <span class="hljs-keyword">Do</span> you offer tours <span class="hljs-keyword">of</span> the brewery?
  &lt;/<span class="hljs-keyword">summary</span>&gt;
     Unfortunately at this <span class="hljs-type">time</span> we <span class="hljs-keyword">do</span> <span class="hljs-keyword">not</span> offer tours <span class="hljs-keyword">of</span> the brewery. We are currently building our taproom so stay tuned.
&lt;/details&gt;
&lt;details&gt;
  &lt;<span class="hljs-keyword">summary</span>&gt;
    Are your beers gluten-free?
  &lt;/<span class="hljs-keyword">summary</span>&gt;
     <span class="hljs-keyword">All</span> <span class="hljs-keyword">of</span> our beers are gluten-free <span class="hljs-keyword">and</span> vegan-friendly!
&lt;/details&gt;
</code></pre><h3 id="8-lessfiguregreater-and-lessfigcaptiongreater">8. <code>&lt;figure&gt;</code> &amp; <code>&lt;figcaption&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML <code>&lt;figure&gt;</code> (Figure With Optional Caption) element represents self-contained content, potentially with an optional caption, which is specified using the (<code>&lt;figcaption&gt;</code>) element. The figure, its caption, and its contents are referenced as a single unit.</em></p>
</blockquote>
<p>Generally the <code>&lt;figure&gt;</code> element encloses an image (although it is not required) and is usually accompanied by a <code>&lt;figcaption&gt;</code> element that provides the description for the element. You could also include other elements such as the <code>&lt;p&gt;</code> tag that is relevant to the figure.</p>
<p>In my experience it is useful to include the <code>&lt;figure&gt;</code> element when you include a piece of content such as an image, diagram, or piece of code that relates to the content you are writing within some article or post. <br /></p>
<p>Example 👇</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>
    Article header
  <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    ...some introductory text
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">figure</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"post.webp"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"a nice descriptive alt text"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">figcaption</span>&gt;</span>
      Photo taken by Kieran Roberts
    <span class="hljs-tag">&lt;/<span class="hljs-name">figcaption</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">figure</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    ..rest of the article
  <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>
</code></pre><h3 id="9-lessblockquotegreater">9. <code>&lt;blockquote&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML <code>&lt;blockquote&gt;</code> Element (or HTML Block Quotation Element) indicates that the enclosed text is an extended quotation. Usually, this is rendered visually by indentation (see Notes for how to change it). A URL for the source of the quotation may be given using the cite attribute, while a text representation of the source can be given using the <code>&lt;cite&gt;</code> element.</em></p>
</blockquote>
<p>Use the <code>&lt;blockquote&gt;</code> when you want to include text in your site that comes from another source. The <code>cite</code> attribute on the <code>&lt;blockquote&gt;</code> element should be used to provide original source of the content. <br /></p>
<p>Example 👇</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">blockquote</span> <span class="hljs-attr">cite</span>=<span class="hljs-string">"link to the souce"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    This is an inspirational quote from another source
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
    Source author, <span class="hljs-tag">&lt;<span class="hljs-name">cite</span>&gt;</span>content of the citation<span class="hljs-tag">&lt;/<span class="hljs-name">cite</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">blockquote</span>&gt;</span>
</code></pre><h3 id="10-lessabbrgreater">10. <code>&lt;abbr&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML Abbreviation element (<code>&lt;abbr&gt;</code>) represents an abbreviation or acronym; the optional title attribute can provide an expansion or description for the abbreviation. If present, the title must contain this full description and nothing else.</em></p>
</blockquote>
<p>This element is nice for providing a simple but effective tooltip effect to acronyms you might have on your site. I have included this attribute myself in my portfolio for things like JavaScript(js). The <code>title</code> attribute provides the full description of the acronym when the users hover over it. <br /></p>
<p>Example 👇</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
  I really enjoy working with<span class="hljs-tag">&lt;<span class="hljs-name">abbr</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"JavaScript"</span>&gt;</span> js <span class="hljs-tag">&lt;/<span class="hljs-name">abbr</span>&gt;</span>and I am now learning <span class="hljs-tag">&lt;<span class="hljs-name">abbr</span> <span class="hljs-attr">title</span>=<span class="hljs-string">"TypeScript"</span>&gt;</span> ts <span class="hljs-tag">&lt;/<span class="hljs-name">abbr</span>&gt;</span>.
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre><h3 id="11-lessfootergreater">11. <code>&lt;footer&gt;</code></h3>
<p><br />Definition by MDN:</p>
<blockquote>
<p><em>The HTML <code>&lt;footer&gt;</code> element represents a footer for its nearest sectioning content or sectioning root element. A footer typically contains information about the author of the section, copyright data, or links to related documents.</em></p>
</blockquote>
<p>Typically people include one <code>&lt;footer&gt;</code> at the bottom of the document but we can also include a <code>&lt;footer&gt;</code> for each <code>&lt;article&gt;</code> or <code>&lt;section&gt;</code> element as long as it does not have another <code>&lt;footer&gt;</code> or <code>&lt;header&gt;</code> element as a descendant. Inside the <code>&lt;footer&gt;</code> you can include any kind of text, images, or links related to the placement of the element.<br /></p>
<p>Examples 👇</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    This is the end of the site
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
</code></pre><p>or </p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">article</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>
    Article header
  <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
    Article content
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
      Article footer
    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">footer</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">article</span>&gt;</span>
</code></pre><h2 id="conclusion">Conclusion</h2>
<p>I hope you have learned something about semantic HTML that you can take with you into future projects to make your markup more accessible for everyone that interacts with your site!</p>
<p>You can follow me <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a> where I'm always active and happy to connect with you!</p>
<p><a href="https://www.buymeacoffee.com/kieran6roberts"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a beer&amp;emoji=🍺&amp;slug=kieran6roberts&amp;button_colour=FFDD00&amp;font_colour=000000&amp;font_family=Cookie&amp;outline_colour=000000&amp;coffee_colour=ffffff" /></a></p>
<p>Thanks for reading 👋</p>
]]></content:encoded></item><item><title><![CDATA[I took part in #5amCoders! These are the benefits I saw from being productive earlier in the day]]></title><description><![CDATA[#5amCoders is a challenge over on Twitter that aims to help you find out the benefits that rising early and being productive can give you. The point of the challenge isn't necessarily to wake up at exactly 5am or even to be writing code. Instead you ...]]></description><link>https://blog.kieranroberts.dev/i-took-part-in-5amcoders-these-are-the-benefits-i-saw-from-being-productive-earlier-in-the-day</link><guid isPermaLink="true">https://blog.kieranroberts.dev/i-took-part-in-5amcoders-these-are-the-benefits-i-saw-from-being-productive-earlier-in-the-day</guid><category><![CDATA[Web Development]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Mon, 26 Apr 2021 08:38:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619419094276/ULm9JNMs_.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>#5amCoders</strong> is a challenge over on Twitter that aims to help you find out the benefits that rising early and being productive can give you. The point of the challenge isn't necessarily to wake up at exactly 5am or even to be writing code. Instead you attempt to rise early and try to be as productive as you can in whatever way is good for you!</p>
<p>I took this challenge very literally as a lot us did 😃. </p>
<p>Defining the target as being productive at 5am was a way for me to hold myself accountable. I had a specific target and I was going to stick to it from Monday to Friday. Every morning I set my alarms between 4:45am and 5:00am and managed to get up and be productive in my own way.</p>
<p>Now that the week is over I want to share my experiences with you. I saw a lot of benefits from the challenge to myself that goes beyond the code.</p>
<p>Don't forget you can join the early risers yourself if you would like to. Check out <a target="_blank" href="https://twitter.com/nitecoda1">@nitecoda1</a> and <a target="_blank" href="https://twitter.com/andresuriegas">@andresuriegas</a> on Twitter as the guys that inspired me to take part!</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/nitecoda1/status/1383035673226190849">https://twitter.com/nitecoda1/status/1383035673226190849</a></div>
<p>Also go and check out this awesome article by another participant in last week's #5amCoders <a target="_blank" href="https://nicoleanalisecox.com/what-i-learned-from-waking-up-at-5am/">What I learned from waking up at 5AM - [Nicole Analise Cox]</a></p>
<p>So let's find out what I learned from the challenge and how it could be helpful to you 👏. </p>
<h2 id="what-i-learned">What I learned</h2>
<ol>
<li>The guilt from doing stuff non-code related was gone</li>
<li>You have more free time</li>
<li>Routine is important</li>
<li>The community support is awesome! </li>
<li>You have to get over the hump</li>
<li>Waking up is not the problem. Going to sleep at the right time is!</li>
<li>It is not for everyone</li>
<li>Will I stick to it?</li>
</ol>
<h3 id="1-the-guilt-from-doing-stuff-non-code-related-was-gone">1. The guilt from doing stuff non-code related was gone</h3>
<p>You might have felt this one yourself. Since learning to code I have been determined to learn as much as I can in the shortest possible time. The downside of this is that when I'm not coding I almost feel guilty about it. My thoughts would something like this 👇 </p>
<blockquote>
<p>Instead of watching this movie I could finish a certain part of my project.</p>
</blockquote>
<p>And this is not a healthy way to live. This was the first and best thing I learned during this challenge. By waking up early and tackling the most difficult tasks first, I have all of my required work finished before I even eat breakfast 😃.</p>
<p>Between 5am and 8am I would finish my intensive coding tasks and for the rest of the day I would feel completely free to do whatever I pleased. <strong>It's and incredible feeling!</strong></p>
<h3 id="2-you-have-more-free-time">2. You have more free time</h3>
<p>Because my required tasks were finished by breakfast I suddenly had a lot more free time on my hands. This allowed me to get back to some of my other hobbies and interests. </p>
<p>I have been learning to play guitar on and off for the last couple of years and this week I found myself playing more than I have been doing recently. On Wednesday I also spent the whole day outside hiking which I loved.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619415422741/z_4_GbrQJ.jpeg" alt="the view from a hillside overlooking the beach and sea on the Anglesey Coastal Path" /></p>
<p>I could happily spend time doing other things I love thinking I had earned it.</p>
<p>It was also beneficial to my health. This challenge helped me get back into exercising after months of procrastination. The release I get from exercising helps keep my mental and physical health in a good place and it's likely I wouldn't have got back into it without the push from <strong>#5amCoders</strong>.</p>
<h3 id="3-routine-is-important">3. Routine is important</h3>
<p>For a lot of people a routine is essential. It helps you stay consistent with your task which is massively important. <strong>#5amCoders</strong> gave me a clear and defined routine that I could hold myself accountable to. Mine looked something like this 👇</p>
<ul>
<li><strong>5am to 8am</strong> - Intensive code related tasks</li>
<li><strong>8am to 10am</strong> - Workout &amp; breakfast</li>
<li><strong>10am onwards</strong> - Anything I wanted</li>
</ul>
<p>I should note that I'm currently spending time at home with the family after returning home from travel. I'm not employed in a full time role otherwise this routine might look a little different 😅.</p>
<p>Check out this article that explains my story from the last two years and how I got into code - <a target="_blank" href="https://blog.kieranroberts.dev/monday-motivation-how-i-got-into-code">Monday motivation - How I got into code</a>.</p>
<p>I could spend the rest of the day doing whatever I liked which included everything from relaxing with family and my girlfriend to writing for you and me here!</p>
<h3 id="4-the-community-support-is-awesome">4. The community support is awesome!</h3>
<p>Just check out this post here which includes a lot of the people taking part in #5amCoders last week 👇</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/nitecoda1/status/1383994015008256002">https://twitter.com/nitecoda1/status/1383994015008256002</a></div>
<p> </p>
<p>It was awesome having so many in the same situation supporting each other. Sometimes we all need a little help from friends.</p>
<p>The major benefit of this is the sense of community accountability. By putting it out there for others to see you are reducing the possibility of changing your mind. If you're the only person you let in on the challenge it is really easy to talk yourself out of it.</p>
<p>If you share the fact you want to take part with others then you will might feel obliged to go further with it and the community will help you get there 🙂.</p>
<h3 id="5-you-have-to-get-over-the-hump">5. You have to get over the hump</h3>
<p>My hump was on Wednesday. What I mean by this is that it was my most difficult day trying to wake up and be productive. It was the final hill I had to climb. This was the only day where I struggled to get out of bed. I have three alarms and on this day I woke up a minute before 5am on the very last alarm. </p>
<p>I must have turned the first two off 😅.</p>
<p>For that morning I struggled to get much work done because I found it hard to concentrate. Though after this day I definitely noticed a change. I was also not the only person who struggled on this day so I believe there is a turning point that you need to push through.</p>
<p>The next two mornings I was far less tired in the early part of the morning and my energy would last further into the day. I managed to be quite productive on both Thursday and Friday.</p>
<h3 id="6-waking-up-is-not-the-problem-going-to-sleep-at-the-right-time-is">6. Waking up is not the problem. Going to sleep at the right time is!</h3>
<p>For me the difficult part of the challenge is not waking up so early. I could set a few alarms and usually jump straight out of bed. However making sure you go to bed at the right time was the challenge because that affects how you will feel in the morning.</p>
<p>We have many distractions in modern day life. Everything from our mobile phones, laptops, computers and televisions. They all contribute to making your sleep more difficult than it otherwise would be. I found this out on Wednesday morning when I stayed up a little too long staring at screens the night before. This may have contributed to my hump day but I think it would have happened regardless.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/Kieran6dev/status/1384721780124831744">https://twitter.com/Kieran6dev/status/1384721780124831744</a></div>
<p>By staying up to late you can still get yourself out of bed if you really want to but you'll feel terrible because of it. Concentration will be difficult and it makes the whole process less valuable to you.</p>
<p>For the most part I tried to tire myself out during the day mostly through exercise so that by 7pm I was already tired and by 9pm I was asleep 😴. I think it definitely helped me.</p>
<h3 id="7-it-is-not-for-everyone">7. It is not for everyone</h3>
<p>I'll admit that it is not suited to everyone. We are all different and what works for one might not work for another. It is up to you as an individual to try things out and make up your own mind.</p>
<p>I've seen many developers say they like coding at night. Some do it because their commitments leave them with no other choice. But if you are a morning person or would like to become one then consider giving it a go because it will do wonders for you and your productivity ✌.</p>
<h3 id="8-will-i-stick-to-it">8. Will I stick to it?</h3>
<p>The magic question ✨</p>
<p>Because of all the benefits I've listed above it would be strange if I reverted back to my old ways right? For the last couple of months I have been waking up much later and usually don't get started with my work until the end of the morning.</p>
<p>I am definitely going to be sticking to an early routine that allows me to complete my intensive tasks early. However this week I'm going to try out a slightly later time so I can see how my body reacts.</p>
<p>This week I am going to be waking up for 6am which still allows me plenty of time to get work done early. Maybe I'll realize that I actually prefer the 5am start. </p>
<p>There's only one way to find out!</p>
<h3 id="conclusion">Conclusion</h3>
<p>If you are interested in trying out the early morning routine with others then check out <a target="_blank" href="https://twitter.com/nitecoda1">@nitecoda1</a> on Twitter or search for <a target="_blank" href="https://twitter.com/search?q=%235amCoders&amp;src=typeahead_click">#5amCoders</a>. You don't have to do it alone!</p>
<p>I hope you enjoyed the article. If you did consider leaving some likes ❤ or let me know about it in the comments 💬. You can also connect with me <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a> where I'm always active.</p>
<p><a href="https://www.buymeacoffee.com/kieran6roberts"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a beer&amp;emoji=🍺&amp;slug=kieran6roberts&amp;button_colour=FFDD00&amp;font_colour=000000&amp;font_family=Cookie&amp;outline_colour=000000&amp;coffee_colour=ffffff" /></a></p>
<p>See you soon 👋</p>
]]></content:encoded></item><item><title><![CDATA[How I improved my portfolio SEO including tips and resources]]></title><description><![CDATA[Last week I decided that I wanted to learn more about SEO which stands for Search Engine Optimization. SEO is defined as the following 👇

SEO stands for Search Engine Optimization, which is the practice of increasing the quantity and quality of traf...]]></description><link>https://blog.kieranroberts.dev/how-i-improved-my-portfolio-seo-including-tips-and-resources</link><guid isPermaLink="true">https://blog.kieranroberts.dev/how-i-improved-my-portfolio-seo-including-tips-and-resources</guid><category><![CDATA[SEO]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[portfolio]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Fri, 23 Apr 2021 08:51:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619167044943/6PPyshhFP.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Last week I decided that I wanted to learn more about SEO which stands for Search Engine Optimization. SEO is defined as the following 👇</p>
<blockquote>
<p>SEO stands for Search Engine Optimization, which is the practice of increasing the quantity and quality of traffic to your website through organic search engine results. - <a target="_blank" href="https://moz.com/learn/seo/what-is-seo">Moz.com</a></p>
</blockquote>
<p>In order to tackle the topic head on I used my portfolio as a project to apply what I was learning into practice. I had already built the portfolio in its current state around two months prior with only minor fixes since. You can check it out below if you'd like.</p>
<p>My live web-developer portfolio - <a target="_blank" href="https://kieranroberts.dev/">kieranroberts.dev</a></p>
<p>The first step would be to test the site in its current state. I started by running it through various free SEO checkers accessible through a simple search and found that there were a lot of issues I was largely unaware of. This is a good representation of my initial score from one of the tests 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619109996188/aLD4D3gxT.jpeg" alt="my seo score before improvements after a test showing an initial score of 67/100" /></p>
<p>After various changes and improvements that were mostly simple fixes I was very happy with my results. Below you can one of the tests carried out after making improvement 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618663176212/YfSHR-wth.jpeg" alt="my seo score after improvements after a test showing a score of 94/100" /></p>
<p>and one more</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618663186660/2EZ9C-K0u.jpeg" alt="my seo score after improvements after a test showing a score of A- with apossible max of A+" /></p>
<p>I still have some improvements to make but it's a step in the right direction ✔.</p>
<p>Before we begin I should clarify this is not a detailed tutorial on SEO topics because I am no expert and I'm still very much learning myself. Rather it is an overlook of some of the things I did to improve my project that you will be able to explore yourself when you've finished with the article.</p>
<p>Let's get into how I did it, what I learned and some useful resources to help you do the same 👏.</p>
<h2 id="content">Content</h2>
<ol>
<li>Testing using basic free SEO tests</li>
<li>On-page SEO improvements<ul>
<li>301 Redirects</li>
<li>Page Content</li>
<li>Sitemap &amp; Robots</li>
<li>Meta Tags &amp; Keywords</li>
</ul>
</li>
<li>Conclusion</li>
</ol>
<h2 id="testing-using-basic-free-seo-tests">Testing using basic free SEO tests</h2>
<p>The first step was to test out the current state of the site SEO so I could see what needed improvement. You can do this using many of the free SEO checkers available with a simple search. If you type 'seo test' or 'seo checker' into google you'll find a bunch of them 👏. All you need to do is to pass your domain into the input provided and sit back while it does the checking for you.</p>
<p>Each of them seemed to suggest different factors to improve so I recommend you use more than one to cover all the bases. I'll list some of the ones I tried out below which gave me actionable feedback to work on.</p>
<ul>
<li><a target="_blank" href="https://www.seobility.net/en/seocheck/">Seobility - SEO Checker</a></li>
<li><a target="_blank" href="https://seositecheckup.com/">SEO Site Checkup</a></li>
<li><a target="_blank" href="https://www.seoptimer.com/">SEOptimer</a></li>
<li><a target="_blank" href="https://www.ionos.co.uk/tools/seo-check">IONOS - SEO Check</a></li>
</ul>
<h2 id="on-page-seo-improvements">On-page SEO improvements</h2>
<p>At this stage I was receiving pretty poor results as you saw above. Around an average of about 65/100 between the different tests. Before we get into the changes I made let's quickly clear up the difference between on-page SEO and off-page SEO.</p>
<p><strong>On-page SEO</strong></p>
<p>Things you can do to help your site's SEO directly on your website to help search engine robots understand and interpret your page. We will focus on this type of SEO in this article!</p>
<p><strong>Off-page SEO</strong></p>
<p>This refers to things you can do to help your site's SEO away from your site such as increasing the quantity of quality backlinks to your site. Check out this for more information <a target="_blank" href="https://moz.com/learn/seo/off-site-seo">Moz - Off-Page SEO</a>.</p>
<p>Now we know i was working on the on-page SEO let's find out what changes I made 👏.</p>
<h3 id="301-redirects">301 Redirects</h3>
<p>These redirects were something I had never considered before. So what's the problem 🤔? Let me show you an example.</p>
<p>Consider my domain available at <code>https://kieranroberts.dev</code>. What would happen if someone searched for the subdomains <code>https://www.kieranroberts.dev</code> or <code>www.kieranroberts.dev</code> which users might do and we didn't set up a redirect for this?</p>
<p>Well your potential users will be left high and dry potentially on a domain that does not exist or perhaps on a page that contains content that has expired! </p>
<p>I want to prefer the domain <code>https://kieranroberts</code> because this and <code>www.kieranroberts.dev</code> are different domains.</p>
<p>So how do we correct this?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618915695759/3mFxe3jiw.jpeg" alt="warning from seo checker saying we are missing redirects" /></p>
<p>✔<strong>Changes I made</strong></p>
<p>I had not setup these redirects at all and it was the first thing I tackled. </p>
<p>Before we get started it is important to note that my site is hosted with Vercel and the domain is registered with Namecheap.</p>
<p>By logging in with Namecheap and clicking on on the <strong>manage</strong> button of your domain, navigate into <strong>Advanced DNS settings</strong>. Here you can manage you domain's host records.</p>
<p>I began by setting up the subdomain for the <code>www.</code> variant as you can see below 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619076464805/EG_5MAqsS.jpeg" alt="a new CNAME record in namecheap for the `www.` subdomain" /></p>
<p>I pass in the value <code>cname.vercel-dns.com</code> provided by Vercel which you can find more information on here <a target="_blank" href="https://vercel.com/docs/custom-domains">Vercel - Custom Domains</a>.</p>
<p>Next up I need to go into my Vercel project dashboard and setup our new sub-domain. Here you can add domains to your project and I add my recently created <code>www.</code> subdomain. Vercel allows you to specify a main domain and any redirects to setup from one to another. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619081573031/VQQ1w0L5m.jpeg" alt="my Vercel domains including main domain and subdomain" /></p>
<p>To find this setting navigate from your dashboard into the project. Then click <strong>settings</strong> - <strong>domains</strong> and you will see this list for your project.</p>
<p>Here you can see <code>kieranroberts.dev</code> specified as the main domain and any searches prefaced by <code>www.</code> will now be redirected to this main domain. Now we should satisfy this simple redirect 👇.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619081831763/LFIdNCa3Z.jpeg" alt="seo tester showing a passing grade for the http redirect" /></p>
<p>and our users will be happy 😃.</p>
<h3 id="page-content">Page Content</h3>
<p>Another major contributor to your on-page SEO relates to the quality of your page content. OK but what is page content? This considers multiple factors based on the content on your page many of which I did not consider when building my site.</p>
<p>Some of the factors include 👇</p>
<ul>
<li>Content relevance</li>
<li>Descriptive image alts included</li>
<li>Suitable headings</li>
<li>Suitable number of internal &amp; external links</li>
<li>Use of keywords</li>
<li>Mobile Optimization</li>
<li>Word count and sentence length</li>
</ul>
<p>and much more. If you run a site through the tests you will see these factors recommended to you with improvements.</p>
<p>✔<strong>Changes I made</strong></p>
<p>Overall the page content of the site was in reasonable shape and there were not many changes I had to make. The quantity of my external and internal links was acceptable and there was enough relevant content using keywords when suitable.</p>
<p>One major issue I did find however was down to missing alt text on my logo images. I have some logo images on my site representing the stack choice for my projects which looks like this 👇.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619085040753/tzYWy25fB.jpeg" alt="brand logos of the tech stack I used for my e-commerce project" /></p>
<p>The SEO check spotted the fact I had an error with the alt text for these logos and I have a lot of them on the home page. This is a big problem because alt text for images is extremely important for your site accessibility. If missing it will negatively impact the SEO of your site. Fortunately is was a simple fix 👏.</p>
<h3 id="sitemap-and-robots">Sitemap &amp; Robots</h3>
<p>This is not some crime fighting duo as the title suggests 😂 .</p>
<p>They are two important parts of giving more information to the browsers in relation to your site. By adding a robots.txt file to the root of our project we can instruct the browser crawlers to which parts of our site we want them to index.</p>
<p>Check out this article for more information <a target="_blank" href="https://yoast.com/ultimate-guide-robots-txt/">Yoast - The ultimate guide to robots.txt</a>.</p>
<p>The sitemap is a file also placed at the root that includes information about the pages/files of your site. This file helps the search engine crawl your site which may be difficult if your working with a very large site and so could very important.</p>
<p>Check out <a target="_blank" href="https://developers.google.com/search/docs/advanced/sitemaps/overview">Google Search Central</a> for detailed information on sitemaps.</p>
<p>✔<strong>Changes I made</strong></p>
<p>My portfolio only includes 6 pages and so the search engines could probably index the site by following the links but I wanted to include basic versions of each mostly as a learning exercise. Initially I was missing both of these files. Since I am still in the process of learning many of these topics, I searched for information on sitemaps and found that there are free online sitemap generators which will create a basic sitemap for you. </p>
<p>This is the one I used <a target="_blank" href="https://www.xml-sitemaps.com/">xml - sitemaps.com</a> which again is just a case of passing in your domain and letting it do the work. In order to make the sitemap available to the search engine you can place it somewhere in the <code>robots.txt</code> file like this 👇</p>
<p><code>Sitemap: https://kieranroberts.dev/sitemap.xml</code></p>
<p>After creating a robots.txt file this is exactly what I did. Similarly to the sitemap it is possible that the <code>robots.txt</code> file is not required for a site like this. I don't have any pages that I would want to block from being indexed such as a login page for example. Even so I wanted to learn and create these files now in case the site ever grows into something larger.</p>
<p>I'm not going to include these here because I'm still unsure of the optimal setup for the robots file. For now it is allowing the search engine crawlers access to all pages as is the default. </p>
<p>The last thing I want is to share a potentially problematic setup that cause major problems so you can use the links provided to do some research you like 🙂. </p>
<p>This is another useful article about the <code>robots.txt</code> file <a target="_blank" href="https://backlinko.com/hub/seo/robots-txt">backlinko - Technical SEO - Robots.txt</a></p>
<h3 id="meta-tags-and-keywords">Meta Tags &amp; Keywords</h3>
<p>It's likely that you've used plenty of meta tags before. Meta tags describe the content of your site in many different possible ways. As well as meta tags we also have the <code>title</code> element and in combination with the <code>&lt;meta name="description" content="..." /&gt;</code> tag they provide the information you see when you search for something in the search engine like this 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619085824573/8KfpNDOEz.jpeg" alt="this is my portfolio in the search results showing the title and description content" /></p>
<p>Other important tags include the the open graph meta tags (og) which allow you to control the content when you share the site in places like Twitter and Facebook. Twitter also has it own tags such as <code>twitter:title</code> but it will use the og tags instead if they are not present.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619087356879/j5aj1Zo8N.jpeg" alt="my hashnode blog when shared using Twitter as an example" /></p>
<p>There is a very useful site available here <a target="_blank" href="https://metatags.io/">metatags.io</a> that allows you to preview your site's meta tags, edit them and generate new ones.</p>
<p>✔<strong>Changes I made</strong></p>
<p>First of all I was able to spot an issue with my social sharing image which was not displaying correctly. This was because I did not place the image in the correct directory. After moving it to the root of the project all was good!</p>
<p>Next up I wanted to improve the quality of my title and description tags for each individual page after suggestions from a few of the tests that included 👇</p>
<ul>
<li>Adding site keywords to title and description</li>
<li>Descriptive title which accurately represents what is on the page</li>
<li>Title and description have optimal character lengths</li>
</ul>
<p>After changes I saw some score improvements although I could still add certain keywords which are currently not used. Finding the right balance of including enough keywords without overdoing it seems tricky</p>
<p>Take a look at this for in depth analysis on the title and description tags <a target="_blank" href="https://www.wordtracker.com/academy/seo/page-optimization/titles-and-descriptions">Wordtracker - Titles and descriptions in SEO</a></p>
<p>Here is the current state of some of my meta tags 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619163957143/BVjeac2rD.jpeg" alt="the current social meta tags I have in my portfolio project" /></p>
<p>Note: this is in React with Next.js using a custom head component where the title and description is passed in as props for the different pages.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I'm still in the process of learning about SEO so if you have any pointers feel free to share them. There are still improvements I know I can make and I look forward it. </p>
<p>I hope you were able to learn something and if you enjoyed please let me know about it.</p>
<p>You can find me <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a> on twitter and if you would like another read check out this article about my personal story of how I got into code below.</p>
<p>Next read - <a target="_blank" href="https://blog.kieranroberts.dev/monday-motivation-how-i-got-into-code">Monday motivation. How I got into code</a></p>
<p><a href="https://www.buymeacoffee.com/kieran6roberts"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a beer&amp;emoji=🍺&amp;slug=kieran6roberts&amp;button_colour=FFDD00&amp;font_colour=000000&amp;font_family=Cookie&amp;outline_colour=000000&amp;coffee_colour=ffffff" /></a></p>
<p>Until next time 👋</p>
]]></content:encoded></item><item><title><![CDATA[Monday motivation - How I got into code]]></title><description><![CDATA[We all need a little Monday motivation from time to time. This week I wanted to share my story of how I got into coding. I think it's cool being able to share something a little more personal and you can get to know me a little better.
I'll keep it s...]]></description><link>https://blog.kieranroberts.dev/monday-motivation-how-i-got-into-code</link><guid isPermaLink="true">https://blog.kieranroberts.dev/monday-motivation-how-i-got-into-code</guid><category><![CDATA[motivation]]></category><category><![CDATA[personal]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Mon, 19 Apr 2021 08:23:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618508763333/GK-H3_f_l.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We all need a little Monday motivation from time to time. This week I wanted to share my story of how I got into coding. I think it's cool being able to share something a little more personal and you can get to know me a little better.</p>
<p>I'll keep it short and sweet because otherwise there would be a lot to tell and I could on for days 😄.</p>
<p>Here's my personal story of how I got into coding!</p>
<h2 id="heading-my-short-story">My Short Story</h2>
<ol>
<li><p>Life before code</p>
</li>
<li><p>I had tried and failed with coding before</p>
</li>
<li><p>Making progress</p>
</li>
<li><p>The end of my travels</p>
</li>
<li><p>And to today</p>
</li>
</ol>
<h2 id="heading-life-before-code">Life before code</h2>
<p>I was an engineering graduate with a lost passion for the subject. In wasn't until my final masters year that I admitted this to myself but I persevered and graduated well. I no longer wanted to be an engineer so I took some time out to travel and have some fun 🤞.</p>
<p>In April 2019 I left the UK to go to New Zealand on a working holiday visa. I packed my things and went alone. It was the best time of my life and I was fortunate to make so many awesome memories and a few less fortunate ones 😅.</p>
<p>Some of the highlights include:</p>
<ul>
<li><p>Almost leaving behind my phone &amp; wallet in Changi Airport security</p>
</li>
<li><p>Tried surfing and ended up in the doctor's office getting stitches in my head</p>
</li>
<li><p>Hiked up as many peaks as possible</p>
</li>
<li><p>Worked many cool jobs including:</p>
<ol>
<li><p>Hostel Receptionist 👨‍💼</p>
</li>
<li><p>Waiter 💁‍♂️</p>
</li>
<li><p>Maitre d' 🤵</p>
</li>
<li><p>Pine tree orchard worker 🌲</p>
</li>
<li><p>Tomato glasshouse worker 🍅</p>
</li>
<li><p>Kiwifruit picking 🥝</p>
</li>
</ol>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618742228280/Qf2n-vyou.jpeg" alt="Me on top of a mountain with a landscape view of Nelson lakes behind" /></p>
<p>I had been working/traveling there for almost a full year when Covid hit. Just before this I started to think about what I wanted to do when I returned. I didn't want to be an electronic engineer anymore. That much I knew.</p>
<p>I'm not sure exactly how it happened but I stumbled on a list of common jobs done by remote workers. In there was <strong>web-development</strong> which from what I remember was talking about Wordpress.</p>
<p>After a little more digging I thought it looked interesting and decided to try out front-end development for myself. At this point I didn't think it was a realistic goal to be honest. Started with some HTML and I got hooked 😄.</p>
<h2 id="heading-i-had-tried-and-failed-with-coding-before">I had tried and failed with coding before</h2>
<p>At university our engineering department was a joint department along with computer science. They gave us 2 computer science modules in our first year. Java basics in semester one and C++ basics in semester two.</p>
<p>❌<strong>I failed the C++ module and narrowly passed Java</strong></p>
<p>I hated it at the time and it showed in my results. This failure was a big motivation for me this time around and I hope if helps you to know that it's OK to fail.</p>
<p>Just try again! ✔</p>
<h2 id="heading-making-progress">Making progress</h2>
<p>Fortunately I had taken my laptop with me on my travels and so three weeks before New Zealand went into lockdown I started with HTML. I loved it and didn't stop for the next 2 months.</p>
<p>I lived through lockdown in a hostel learning to code for what seemed like 6-8 hours a day. I saw an opportunity to use my time productively so I just went for it. I was thinking 👇</p>
<blockquote>
<p>I'm not going to get a better chance to make this change and give it all my time so let's go!</p>
</blockquote>
<p>My girlfriend who I met on my travels 8 months prior was incredibly supportive as I made my way from HTML to CSS to JavaScript and I felt like I managed to cover a lot during this time. After lockdown was up we moved on to look for more full-time work which was needed since it was almost 5 months since the last paycheck.</p>
<p>For the next almost 4 months I was working full time from 8 in the morning to 5 in the afternoon on weekdays and part time for an hour or two afterwards on hostel reception. I didn't have much time to code and so I did what I could 🙂.</p>
<p>I would wake up at 5am every weekday to get two hours of code in before I left because I was afraid that if I stopped that I wouldn't come back. I'm so happy I did this because it meant I was able to keep some consistency and managed to retain a lot of the things I learned up to that point.</p>
<p><strong>Some form of consistency is crucial if you're going to be successful.</strong></p>
<p>During this time I also experienced some problems with my laptop and through my own stupid fault I lost all of my practice projects and code from the first 4 months of learning. I didn't back things up and I hadn't started using Github yet. My laptop went down for around three months until I could get it fixed and my girlfriend was kind enough to let me use hers.</p>
<p>Despite the problems I kept on going!</p>
<h2 id="heading-the-end-of-my-travels">The end of my travels</h2>
<p>At the end of September our visa extensions were up and we were forced to say goodbye to New Zealand. If it was our choice we would have loved to continue our travels for a bit longer and see other countries. But it wasn't to be!</p>
<p>We headed back home in what felt like an apocalyptic movie. Transiting through Brisbane Airport I have never experienced such a strange and eerily quiet atmosphere. I went to stay with my girlfriends family for a few months and this is when my coding started to take off 🚀. Now I was beginning to understand things in greater depth.</p>
<p>I got a far better understanding of JavaScript and started learning React, Next.js and several other tools and libraries. I was on fire for these four months. It was my mission to build my own projects including a portfolio so I would have the option of applying for roles.</p>
<p>It was like my own personal 4 month bootcamp 😄.</p>
<p>Unfortunately we also somehow caught Covid and tested positive three days before our flight back to the UK meaning I was forced to overstay my allowed time in Croatia. Fortunately the only damage was to our bank accounts and all else was well.</p>
<p>A few weeks later I made it home safe and healthy two years after leaving and now with a new career path and a love for coding!</p>
<h2 id="heading-and-to-today">And to today</h2>
<p>Now over a year since I started coding and I still haven't had a desk to work at 😂. I've learned largely on my travels usually from the comfort of a bed or a couch with my laptop. All because I love what I do.</p>
<p>I'm currently spending some time at home with the family and after this I'll be applying for my first web-development role. I was fortunate to have so much free time to put all my efforts into it and I'm inspired by others who are learning while maintaining full time work and other commitments. You're the real hero 😉.</p>
<p>Now you know a little more about me feel free to introduce yourself below or share something about you. I'd love to get to know you all a little better.</p>
<p>If you would like another read check out my article where I explain in steps how I go about writing articles for my blog <a target="_blank" href="https://blog.kieranroberts.dev/my-process-for-writing-blog-articles-with-actionable-steps-you-can-follow">My process for writing blog articles with actionable steps you can follow.</a></p>
<p><a target="_blank" href="https://www.buymeacoffee.com/kieran6roberts">![](https://img.buymeacoffee.com/button-api/?text=Buy me a beer&amp;emoji=🍺&amp;slug=kieran6roberts&amp;button_colour=BD5FFF&amp;font_colour=ffffff&amp;font_family=Poppins&amp;outline_colour=000000&amp;coffee_colour=FFDD00 align="left")</a></p>
<p>Thanks for reading!! 👋</p>
]]></content:encoded></item><item><title><![CDATA[How to setup path aliases for development with Next.js & Jest]]></title><description><![CDATA[TLDR: Update (2025)
Next.js
Configure the baseUrl in the tsconfig/jsconfig along with your desired paths:
// tsconfig/jsconfig.json
{
  "compilerOptions": {
    "@/*": ["./src/*"]
  }
}

Jest
Install jest and jest-environment-jsdom:
pnpm install -D j...]]></description><link>https://blog.kieranroberts.dev/how-to-setup-path-aliases-for-development-with-nextjs-and-jest</link><guid isPermaLink="true">https://blog.kieranroberts.dev/how-to-setup-path-aliases-for-development-with-nextjs-and-jest</guid><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Jest]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Fri, 16 Apr 2021 09:37:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618506905904/p7ivpv3Ow.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-tldr-update-2025">TLDR: Update (2025)</h2>
<h3 id="heading-nextjs">Next.js</h3>
<p>Configure the <code>baseUrl</code> in the <code>tsconfig/jsconfig</code> along with your desired paths:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// tsconfig/jsconfig.json</span>
{
  <span class="hljs-string">"compilerOptions"</span>: {
    <span class="hljs-string">"@/*"</span>: [<span class="hljs-string">"./src/*"</span>]
  }
}
</code></pre>
<h3 id="heading-jest">Jest</h3>
<p>Install <code>jest</code> and <code>jest-environment-jsdom</code>:</p>
<pre><code class="lang-bash">pnpm install -D jest jest-environment-jsdom
</code></pre>
<p>Generate a Jest configuration:</p>
<pre><code class="lang-bash">pnpm create jest@latest
</code></pre>
<p>Update the jest config file to use <code>next/jest</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Config } <span class="hljs-keyword">from</span> <span class="hljs-string">'jest'</span>
<span class="hljs-keyword">import</span> nextJest <span class="hljs-keyword">from</span> <span class="hljs-string">'next/jest.js'</span>

<span class="hljs-keyword">const</span> createJestConfig = nextJest({
  dir: <span class="hljs-string">'./'</span>,
})

<span class="hljs-keyword">const</span> config: Config = {
  coverageProvider: <span class="hljs-string">'v8'</span>,
  testEnvironment: <span class="hljs-string">'jsdom'</span>,
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createJestConfig(config)
</code></pre>
<p>Finally match the paths option from your <code>tsconfig/jsconfig</code> to the Jest <code>moduleNameMapper</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Config } <span class="hljs-keyword">from</span> <span class="hljs-string">'jest'</span>
<span class="hljs-keyword">import</span> nextJest <span class="hljs-keyword">from</span> <span class="hljs-string">'next/jest.js'</span>

<span class="hljs-keyword">const</span> createJestConfig = nextJest({
  dir: <span class="hljs-string">'./'</span>,
})

<span class="hljs-keyword">const</span> config: Config = {
  coverageProvider: <span class="hljs-string">'v8'</span>,
  testEnvironment: <span class="hljs-string">'jsdom'</span>,
  moduleNameMapper: {
    <span class="hljs-comment">// Your desired paths</span>
    <span class="hljs-string">"^@/src(.*)$"</span>: <span class="hljs-string">"&lt;rootDir&gt;/src/$1"</span>,
  },
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> createJestConfig(config)
</code></pre>
<h2 id="heading-original-article">Original article</h2>
<p>I wanted to share how you can setup path aliases for your imports. Sometimes you end up with various levels of file nesting and this can get messy fast.</p>
<p>Fortunately for us both Next.js and Jest support adding path aliases so we don't end up with something like this 👇</p>
<p><code>import Component from "../../../Component/Component";</code></p>
<p>For any medium to large react project you will have realized that due to the component nature of the library we end up doing a lot of importing. Let's find out how we can change the above into this 👇</p>
<p><code>import Component from "@/Component/Component";</code></p>
<p>for both development in Next.js and for our tests using the test Framework Jest.</p>
<h2 id="heading-nextjs-1">Next.js</h2>
<h3 id="heading-step-1-create-a-next-app">Step 1 - Create a Next app</h3>
<p>I'll start with a brand new project so you can see all the steps. For this I will create a new Next application using <code>create-next-app</code> so we can get up and running quickly. Start by running <code>npx create-next-app</code>. This is the basic file structure that comes with <code>create-next-app</code> 👇.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618562840915/3r7FQoPoD.jpeg" alt="create-next-app starter file structure" /></p>
<h3 id="heading-step-2-create-some-files-to-import">Step 2 - Create some files to import</h3>
<p>Next I'm going to create a <code>src</code> folder which is where I normally store the apps components, hooks etc. Let's add some files to our <code>src</code> folder to simulate how a real project might look 👇 .</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618563348642/MK3t-u3JX.jpeg" alt="our file structure after adding a src folder" /></p>
<p>Here I have created the following files</p>
<ul>
<li><p><code>Header.js</code> and <code>Button.js</code> components inside a <code>components</code> folder</p>
</li>
<li><p><code>__tests__</code> folder with our component test files</p>
</li>
<li><p><code>useStorage.js</code> hook inside a <code>hooks</code> folder</p>
</li>
<li><p><code>config.js</code> inside a <code>lib</code> folder</p>
</li>
</ul>
<p>The contents of the files is not important. As long as you export something from your components which I will show you in a second.</p>
<h3 id="heading-step-3-specify-our-path-aliases">Step 3 - Specify our path aliases</h3>
<p>First we need to create a <code>jsconfig.json</code> file (or <code>tsconfig.json</code> if you're using TypeScript) and setup our path aliases. My setup looks like this 👇.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618563426752/Njv1dGes8.jpeg" alt="My jsconfig.json file with path alias setup" /></p>
<p>Here we can specify a base url that we start our import from. Commonly you will see the root <code>.</code> specified.</p>
<p>Next we can specify path aliases for our paths to the different folder locations. <code>paths</code> is an object where we start with our path alias and assign it an array of paths. A common syntax of alias is in the format <code>"@/fileName/"</code> but you can use anything you'd like.</p>
<p>Let's take our <code>components</code> alias as an example. We are saying we want to match the alias <code>"@/components/"</code> to the path <code>src/components/</code> folder.</p>
<h3 id="heading-step-4-checking-that-our-aliases-work">Step 4 - Checking that our aliases work</h3>
<p>Now let's add some basic code to our components:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618338169271/DcYMjLHaM.jpeg" alt="basic button component exporting a button" /></p>
<p>and the header is the same except it exports a <code>&lt;h2 /&gt;</code> tag 🙂. We can import them into other pages/components using the path aliases like this 👇.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618337488284/bwPgcyit9.jpeg" alt="importing our components using our path aliases" /></p>
<p>And to show you everything is working as expected in the browser.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618338418529/FTbbCAtRl.png" alt="Our button and header components in the browser" /></p>
<p>If we had code in our other files that we created inside our <code>hooks</code> and <code>lib</code> files we could import them like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618337724053/ihYD6pMdz.jpeg" alt="importing something from our hooks and lib folders using path aliases" /></p>
<p>Of course we don't have anything in there but I wanted to show this is how you would import from those places.</p>
<h2 id="heading-jest-1">Jest</h2>
<h3 id="heading-step-1-install-and-configure-the-necessary-files">Step 1 - Install and configure the necessary files</h3>
<p>I wanted to add this extra step for two reasons</p>
<ol>
<li><p>You should be testing your apps</p>
</li>
<li><p>It's good to keep your imports consistent across development code and tests</p>
</li>
</ol>
<p>First let's download the packages we need to work with Jest. I will install the following packages:</p>
<ul>
<li><p><code>jest</code></p>
</li>
<li><p><code>babel-jest</code></p>
</li>
</ul>
<p><code>jest</code> if the primary package we need to install in order to use the Jest testing framework. While <code>babel-jest</code>will help transform our code so we can include things like ES modules <code>import</code> syntax in our test files.</p>
<p>We also have to configure a <code>.babelrc</code> file with the following setup 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618502188375/dwxvuSgqJ.jpeg" alt="configure file for babel to work with next.js" /></p>
<p>What we are doing here is telling babel to use the custom preset for Next.js.</p>
<p>Next I'll add a <code>test</code> script to the <code>package.json</code> file so we can run our test suites like this 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618505514432/ffsWKPgBy.jpeg" alt="Our test script for jest in the package.json file" /></p>
<h2 id="heading-step-2-add-our-jestconfigjs">Step 2 - Add our <code>jest.config.js</code></h2>
<p>Finally we can now get on to our aliases in Jest 👏. We first need to create a <code>jest.config.js</code> file. There are a couple of options we will pass in here and it looks something like this 👇.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618502684515/bb23gvp0V.jpeg" alt="Our jest.config file with path aliases setup along with ignore files and babel-jest transform" /></p>
<p>The first thing we do is use the <code>testPathIgonrePattern</code> which is defined as an array of strings that specify which paths we would like to skip when testing. Here we include the <code>node_modules</code> directory and the <code>.next</code> directory which contains our build files.</p>
<p>We don't want Jest looking in these directories for tests and we specify these paths starting at the root <code>&lt;rootDir&gt;</code>.</p>
<p>Next comes our path aliases. We need to use the <code>moduleNameMapper</code> property to map our aliases. It is a map of regular expressions and in this case we setup our three current aliases of <code>@/components</code> <code>@/hooks</code> and <code>@/lib</code>.</p>
<p>You can do this for any path you are likely to be importing regularly. If you wanted to move on to integration tests and import you pages into tests you could do the same for example <code>@/page</code>.</p>
<p>The order in which you setup these aliases could matter however. These patterns are checked by Jest from top to bottom therefore you have more specific rules you should include them first.</p>
<p>For more information check out the <a target="_blank" href="https://jestjs.io/docs/configuration#modulenamemapper-objectstring-string--arraystring">Jest - Configuring Jest</a>.</p>
<p>Now if we add an empty test block to one of our files and attempt the import with the alias we won't receive any errors after running out test script. Our imports are now working as expected in Jest ✔.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618566751895/9nBOaygXD.jpeg" alt="Using our path alias successfully in Jest" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Set up path aliases for cleaner imports in Next.js and Jest by configuring the <code>baseUrl</code> and <code>paths</code> in your <code>tsconfig/jsconfig.json</code>. For Jest, install necessary packages, configure Babel, and set up a <code>jest.config.js</code> file with <code>moduleNameMapper</code> to map aliases for consistent imports across development and tests.</p>
]]></content:encoded></item><item><title><![CDATA[I have been trying out Figma! How a little design knowledge will make you a better developer]]></title><description><![CDATA[Ever since I started development I have struggle with my personal project designs. To correct this I decided to try out a design tool called Figma. I love building projects but I never have a design to work from. I would just make things up as I go w...]]></description><link>https://blog.kieranroberts.dev/i-have-been-trying-out-figma-how-a-little-design-knowledge-will-make-you-a-better-developer</link><guid isPermaLink="true">https://blog.kieranroberts.dev/i-have-been-trying-out-figma-how-a-little-design-knowledge-will-make-you-a-better-developer</guid><category><![CDATA[projects]]></category><category><![CDATA[Design]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[Sass]]></category><dc:creator><![CDATA[Kieran Roberts]]></dc:creator><pubDate>Mon, 12 Apr 2021 10:17:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1618134894542/bMby0AO7-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ever since I started development I have struggle with my personal project designs. To correct this I decided to try out a design tool called <strong>Figma</strong>. I love building projects but I never have a design to work from. I would just make things up as I go which led to reverting hours worth of progress on lost ideas.</p>
<p>I thought <strong><em>'time for a change!'</em></strong> 👏.</p>
<p>I have always been interested in art &amp; design and I loved it at school. So I thought I could apply this using Figma. First I would design a desktop landing page for a fictional Software As A Service (SaaS) product with Figma and then build it with code.</p>
<p>I'm not saying you need to go out and put all of your focus on web-design as a developer. However a little knowledge will make you a <strong>better</strong> developer. If you start working in the industry it is possible you'll end up working with a web designer.</p>
<p>This means you'll start developing to satisfy some design right? 🙂</p>
<p>All of this got me thinking about the benefits of learning a little design for a developer. Let's explore these benefits and the project that inspired them.</p>
<h2 id="heading-my-figma-landing-page-project">My Figma landing page project</h2>
<p>First let me share the deployed project. This is the hero page at desktop size 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618056889643/-A0BTeA4L.png" alt="The hero of my personal design/build project at desktop size" /></p>
<p>and part of the featured services section 👇.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618073570400/5i3G-x2h2.png" alt="Part of the features section of my project" /></p>
<p>I wanted to keep the stack simple. No need for any complicated frameworks here. It is built with 👇</p>
<ul>
<li><p>Figma for design</p>
</li>
<li><p>HTML</p>
</li>
<li><p>SCSS</p>
</li>
<li><p>Vanilla JS (Not required. Site functions the same without it)</p>
</li>
<li><p>Snowpack</p>
</li>
</ul>
<p>Live site is available here 👉<a target="_blank" href="https://kieran-sass-landing.netlify.app/">kieran-sass-landing.netlify.app</a></p>
<p>Code is available here 👉 <a target="_blank" href="https://github.com/kieran6roberts/Kieran-SaaS-Landing">github repo</a></p>
<p>And the lighthouse scores 👇.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618072962272/cgDGqg_zV.jpeg" alt="Lighthouse scores for my project showing a score of 100 for each category" /></p>
<p>I still have a few improvements to make. These include:</p>
<ul>
<li><p>Reducing font sizes</p>
</li>
<li><p>Altering the hero content</p>
</li>
<li><p>Filling some of the whitespace in the features section after hero</p>
</li>
<li><p>Maybe some subtle animations</p>
</li>
</ul>
<p>Let me explain why the benefits you will see with a little design knowledge and how working from design improved my development efficiency 👏.</p>
<h2 id="heading-content">Content</h2>
<ol>
<li><p>You'll shorten your development time by using a design tool</p>
</li>
<li><p>Your project will look much better from the first version</p>
</li>
<li><p>You get practice for when you work with designers</p>
</li>
<li><p>Your eye for detail will improve</p>
</li>
<li><p>You start learning a new skill</p>
</li>
<li><p>Maybe your a better designer than developer</p>
</li>
</ol>
<h3 id="heading-1-youll-shorten-your-development-time-by-using-a-design-tool">1.) You'll shorten your development time by using a design tool</h3>
<p>If you're at least a little familiar with the basics of your design tool you should be able to speed up your development time. It took me a couple of hours to get accustomed to Figma before I started on the project.</p>
<p>I'm definitely no expert with design or Figma but I was quickly able to throw things on the page and piece something together. I was surprised at how fast I was able to do this and that it actually looked pretty good 😃. This couple of hours I spent designing the landing page saved me so much more time in development.</p>
<p>It is much easier to build when you have a full design to reference. I was able to visualize the different components I had meaning I could group common styles together in advance.</p>
<p>You will completely avoid situations where you decide to revert changes and lose potentially hours worth of work. I found it to be much more effective having my design side-by-side with the code and I don't ever think I can go back for any significant project 👏.</p>
<h3 id="heading-2-your-project-will-look-better-from-the-first-version">2.) Your project will look better from the first version</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618151882304/XPd1niFiQ.png" alt="The start of my pricing section for my landing page project" /></p>
<p>Leading on from the previous point your project UI will be much improved from the start. Trying out more complex designs is much quicker inside Figma. You can create all kinds of different shapes in seconds and move things around at will.</p>
<p>In previous projects the first versions of my UI design were pretty poor. I can't tell you how many times I've changed things around in my portfolio. Much of this could have been avoided if I started with a design in mind.</p>
<p>Be honest, how many times have you changed around your portfolio design? 😄</p>
<p>And your UI is vitally important especially if you expect to retain users on your site. Bad UI is one of my biggest turn offs for a website and spending a little time working out your design first will pay off later 😃.</p>
<h3 id="heading-3-you-get-practice-for-when-you-work-with-designers">3.) You get practice for when you work with designers</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618152992706/MEK9-Hf44.png" alt="My pricing section price tier cards" /></p>
<p>It is likely that at some point you will end up working with a web-designer. Having a little design knowledge will help you provide meaningful feedback and suggest changes etc.</p>
<p>Sometimes things that seem good in design doesn't translate well into code. Maybe it's inefficient or negatively affects other parts of the site. Whatever it is being able to recognize this is a useful skill to have to avoid losing hours of progress.</p>
<h3 id="heading-4-your-eye-for-detail-will-improve">4.) Your eye for detail will improve</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618152712747/iTEmTd24F.png" alt="The final call to action on my landing page project" /></p>
<p>I found that creating a design from scratch focused my eye for detail. You are forced to ensure that every item is aligned as it should be and you'll start noticing when things are not. Because you don't have to write the code immediately there are less distractions and you can focus on one element at a time.</p>
<p>I realized I would focus in on factors like colors, typography and spacing making sure everything was exactly as I wanted and although not perfect it is a hell of a lot better that it otherwise would have been.</p>
<h3 id="heading-5-you-start-learning-a-new-skill">5.) You start learning a new skill</h3>
<p>As developers we thrive on learning new skills. For me it's one of the biggest motivations because I love learning new things. Web-design is a skill that ties in closely with development. They complement each other 🤝.</p>
<p>If you think about it every time you build a project that includes a front-end you will be working with design whether you use a design tool or work in the browser.</p>
<p>It is very possible that one day you might be working on a solo project. Maybe it's some service that you intend to run as business. If this is the case you'll want to make sure your design is as good as it can possibly be because a bad UI will not get you anywhere.</p>
<h3 id="heading-6-maybe-your-a-better-designer-than-developer">6.) Maybe your a better designer than developer</h3>
<p>This one is a stretch but you never know 😉. Maybe you realize you prefer design. It takes very little time to at least try it out and it will definitely make you are more well rounded developer.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1618152898585/8VD5EYlh1.png" alt="The footer section of my landing page personal project" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I really loved this project! Creating the design was a lot of fun and actually a lot faster than I imagined. It's not perfect nor near the level of a designer but it's something I'm proud of 😄.</p>
<p>I will absolutely be using a design tool from now on going into personal projects after seeing the benefits to my work.</p>
<p>If you enjoyed the article please leave a like or let me know about it <a target="_blank" href="https://twitter.com/Kieran6dev">@Kieran6dev</a>. I really love hearing your thoughts and comments.</p>
<p>Until next time 👋.</p>
<p><a target="_blank" href="https://www.buymeacoffee.com/kieran6roberts">![](https://img.buymeacoffee.com/button-api/?text=Buy me a beer&amp;emoji=🍺&amp;slug=kieran6roberts&amp;button_colour=BD5FFF&amp;font_colour=ffffff&amp;font_family=Poppins&amp;outline_colour=000000&amp;coffee_colour=FFDD00 align="left")</a></p>
]]></content:encoded></item></channel></rss>