<?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[James Wallis's blog on Hashnode]]></title><description><![CDATA[James Wallis's blog on Hashnode]]></description><link>https://hashnode.wallis.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 08:22:18 GMT</lastBuildDate><atom:link href="https://hashnode.wallis.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[I built an advanced Dev.to dashboard with historic data using Next.js and Azure Functions 📈]]></title><description><![CDATA[Do you ever watch your view count rise and wonder which posts are being read the most? I know I did. 
That is one reason why I chose to develop my own Dev.to analytics dashboard to display historical data such as view, reaction and follower increase ...]]></description><link>https://hashnode.wallis.dev/i-built-an-advanced-devto-dashboard-with-historic-data-using-nextjs-and-azure-functions</link><guid isPermaLink="true">https://hashnode.wallis.dev/i-built-an-advanced-devto-dashboard-with-historic-data-using-nextjs-and-azure-functions</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[DEVCommunity]]></category><category><![CDATA[React]]></category><category><![CDATA[analytics]]></category><category><![CDATA[Developer]]></category><dc:creator><![CDATA[James Wallis]]></dc:creator><pubDate>Tue, 06 Apr 2021 22:55:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1617751226160/OvLboV4mv.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Do you ever watch your view count rise and wonder which posts are being read the most? I know I did. </p>
<p>That is one reason why I chose to develop my own Dev.to analytics dashboard to display historical data such as view, reaction and follower increase over the last 24 hours, 7 days and 30 days.</p>
<p>You can view the live dashboard here: https://devto-analytics.wallis.dev. <em>You'll notice that it's styled to look like Dev.to.</em></p>
<blockquote>
<p><em>Incredibly, Dev.to has recently added <a target="_blank" href="https://dev.to/dashboard/analytics">their own analytics page</a> as well as having an "analytics" option on each post (next to edit, manage). There are some differences between the built-in dashboard and mine.</em></p>
</blockquote>
<hr />
<h2 id="why-build-an-analytics-dashboard">Why build an analytics dashboard?</h2>
<p>I really enjoy posting on Dev.to. I <a target="_blank" href="https://dev.to/jameswallis/i-completely-rewrote-my-personal-website-using-dev-to-as-a-cms-2pje">recently rewrote my whole website to use Dev.to as a CMS</a> to enable myself to continue posting here while using my own site as the canonical URL.</p>
<p>I wanted, however, to see more in-depth information about each post such as view, reaction and follower increases over a given period of time. By default, I didn't have access to any data that would allow me to calculate any increases.</p>
<p>As a result, I ended up building my own analytics dashboard using Next.js, Tailwind CSS, Recharts, the Dev.to API and Azure Functions &amp; Cosmos DB.</p>
<p><a target="_blank" href="https://github.com/james-wallis/devto-analytics">You can view the code on GitHub.</a></p>
<hr />
<h2 id="how-i-source-the-historical-data">How I source the historical data</h2>
<p>If you've used the Dev.to API before, you'll have noticed that, for the moment, you're unable to access any historical data. This makes it difficult to know how popular a post is over a given period of time (before the built-in analytics).</p>
<p>To record my historical data I created an <a target="_blank" href="https://docs.microsoft.com/en-us/azure/azure-functions/">Azure Function</a> that saves my latest <a target="_blank" href="https://docs.forem.com/api/#operation/getUserAllArticles">article</a> and <a target="_blank" href="https://docs.forem.com/api/#operation/getFollowers">follower</a> data, gathered using the Dev.to API, to a <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cosmos-db/introduction">Azure Cosmos Database</a>. The functions that save my article and follower data run every hour.</p>
<p>In addition, I created a couple of HTTP Azure Functions so that I can access the data.</p>
<p>If you're wondering, I'm entirely within the free tier on Azure - so it isn't costing me anything to record my historic data.</p>
<p><a target="_blank" href="https://github.com/james-wallis/devto-analytics/tree/main/azure-functions">You can view the Azure Functions implementation on GitHub.</a></p>
<hr />
<h2 id="the-dashboard">The dashboard</h2>
<p>The dashboard consists of three pages:</p>
<ol>
<li><strong>Home/overview page</strong> - basically an overview of my current stats and periodical view/reaction/comment/follower increases</li>
<li><strong>Breakdown graphs page</strong> - Charts that break down stats from the overview page so that I can easily see information such as what articles have been most read this week.</li>
<li><strong>Summary graphs page</strong> - Charts that display the increase of views/reactions/followers from hour-to-hour and day-to-day</li>
</ol>
<p><a target="_blank" href="https://github.com/james-wallis/devto-analytics/tree/main/dashboard">You can view the dashboard implementation on GitHub.</a></p>
<h3 id="homeoverview-pagehttpsdevto-analyticswallisdev"><a target="_blank" href="https://devto-analytics.wallis.dev/">Home/overview page</a></h3>
<p>The dashboard's UI is based heavily on Dev.to's styling. I wanted it to appear as an extension to Dev.to's current implementation. The home page is styled to look like an advanced version of the <a target="_blank" href="https://dev.to/dashboard">Dev.to dashboard</a> page that contains your general stat overview (total page views, reactions, comments) and a list of any draft and published articles. </p>
<p>Moreover, it was a lot of fun essentially cloning Dev.to to try to make the dashboard look as alike as possible. Try switching between the dashboard home page and your Dev.to dashboard in different tabs!</p>
<p>On my dashboard, I've added follower and last posted date to the overview stats - as I care about them more than "Listing's created" and "Credits available". Most overview stats also contain two pieces of historical data. For page views, for example, I'm displaying the total page view increase for the past 24 hours and the past 7 days.</p>

  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nvh0zb8s15smg9dbnt8w.png" />
  Overview stats


<p>Looking further down the page, you'll see a sortable list of my published posts containing the usual stats for each (views, reactions and comments). I've also added the 24 hours, 7 days and 30 day increase for each stat, for every post.</p>

  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ijg08ez02uey0j31c2e.png" />
  Individual post stats


<h3 id="breakdown-graphs-pagehttpsdevto-analyticswallisdevgraphsbreakdown"><a target="_blank" href="https://devto-analytics.wallis.dev/graphs/breakdown">Breakdown graphs page</a></h3>
<p>This page displays graphs that break down increases for views, reactions and comments. Its purpose is to show me what posts have been most popular over a given period of time.</p>

  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zlu3ch7aer2clvh6i3fs.png" />
  Hovering over a breakdown graph


<p>While its styling is based on Dev.to, unlike the home page I haven't copied a specific Dev.to page.</p>
<h3 id="summary-graphs-pagehttpsdevto-analyticswallisdevgraphssummary"><a target="_blank" href="https://devto-analytics.wallis.dev/graphs/summary">Summary graphs page</a></h3>
<p>The final page contains graphs that details how views, reactions and comments have increased over a period of time. Using this page I can interpret such information as if my follower increases have stagnated or are increasing at a good level.</p>

  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k4410vfltycevkwu7uqq.png" />
  Page view trends over the past 24 hours and 7 days


<h3 id="difference-between-my-dashboard-and-the-built-in-analytics-page">Difference between my dashboard and the built-in analytics page</h3>
<p>If you've read this far you're likely wondering what the difference between my dashboard and the built-in Dev.to analytics dashboard. The following is my opinion, let me know in the comments if you disagree.</p>
<p>What Dev.to built-in analytics does better:</p>
<ol>
<li><p>Dev.to has access to a lot more historical data so they can report your page views way back</p>
<p> <em>I'm bias, let me know if you think there are more than this.</em></p>
</li>
</ol>
<p>What my dashboard does better:</p>
<ol>
<li>Integrates increases/historical data into the main dashboard screen better. I'd love it if my <a target="_blank" href="https://dev.to/dashboard">Dev.to dashboard</a> displayed my daily, weekly and monthly increases for each post.</li>
<li>Displays follower and last posted date in the overview stats - I don't care about listings or credits. Moreover, I'd love to display my current posting streak instead of the time since I last posted. This would help me ensure I'm on track for my 16-week badge.</li>
<li>The breakdown page makes it easy to see why my view count has risen.</li>
</ol>
<hr />
<h2 id="technical-details">Technical details</h2>
<ul>
<li><a target="_blank" href="https://nextjs.org/">Next.js</a> - powers the dashboard.<ul>
<li>The built-in data fetching method <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation"><code>getStaticProps</code></a> is used to preload the article and follower data at build time. <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration">Incremental Static Regeneration</a> rebuilds the page to minimise how out of date the initially served page is.</li>
<li><a target="_blank" href="https://swr.vercel.app/"><code>useSWR</code></a> fetches the current article and follower data once the page has loaded.</li>
<li>Combining <code>getStaticProps</code> and <code>useSWR</code> means that the dashboard loads fast, but will always displays the most up-to-date data.</li>
</ul>
</li>
<li><a target="_blank" href="https://docs.microsoft.com/en-us/azure/azure-functions/">Azure Functions</a> and <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cosmos-db/introduction">Azure Cosmos DB</a><ul>
<li>Saves historical data gathered using the Dev.to API.</li>
<li>Serves the data via a HTTP route for the UI to consume.</li>
</ul>
</li>
<li><a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a> - styling</li>
<li><a target="_blank" href="https://react-icons.github.io/react-icons/">React-icons</a> - various icons such as the question mark and GitHub icon on the navigation bar. </li>
<li><a target="_blank" href="https://recharts.org/">Recharts</a> - the chart library used on the graph pages</li>
<li><a target="_blank" href="https://www.typescriptlang.org/docs/handbook/project-references.html">TypeScript project references</a><ul>
<li>Enable the sharing of TypeScript interfaces, and other code, between the dashboard and the Azure Functions codebases.</li>
<li><a target="_blank" href="https://github.com/james-wallis/devto-analytics/tree/main/common">View in the <code>common</code> directory of the GitHub repository.</a></li>
<li>For more information on TypeScript project references, <a target="_blank" href="https://dev.to/jameswallis/using-typescript-project-references-to-share-common-code-p8o">read my post on them</a>.</li>
</ul>
</li>
</ul>
<hr />
<h2 id="future-improvements">Future improvements</h2>
<p>There are few features that I want to add to the dashboard:</p>
<ul>
<li>Calculate and display my posting streak (by week) in the stat overview - should help with the 16-week streak badge.</li>
<li>Average stats - such as average page views or followers gained per day.</li>
<li>Predicted stats - days until I hit 100,000 page views for example.</li>
</ul>
<hr />
<h2 id="summary">Summary</h2>
<p>In this post, I've introduced <a target="_blank" href="https://devto-analytics.wallis.dev">my Dev.to analytics dashboard</a> that helps me understand how my stats are changing over time and identify popular posts.</p>
<p>Would you like to make your own dashboard like this? Have any thoughts on the dashboard? Let me know in the comments!</p>
<p>If you are looking to fork/clone my <a target="_blank" href="https://github.com/james-wallis/devto-analytics">GitHub repository</a> then I'll need to make the documentation a little better 😅 - it's pretty straightforward to do once your Azure Functions/Cosmos DB is set up. </p>
<p><strong>P.S. Why isn't it password protected?</strong></p>
<p>I'm aware that by posting this blog I'm allowing access to my private post data to anyone who stumbles across it. I'm OK with this because: </p>
<ol>
<li>I want to show the dashboard off as a portfolio piece</li>
<li>I want others to be able to copy/clone it so that they can see their own historical Dev.to data</li>
<li>I couldn't come up with a reason to hide it. I understand why it is private by default but in my case, I'm happy with others seeing it.</li>
</ol>
<hr />
<p>Let me know what you think of <a target="_blank" href="https://devto-analytics.wallis.dev/">my Dev.to analytics dashboard</a>!</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Using TypeScript Project References to share common code]]></title><description><![CDATA[Ever wondered if you can share interfaces, types and functions between TypeScript projects?
I'm currently developing a project consisting of two separate TypeScript applications, one being a React.js dashboard and the other an Azure Function app writ...]]></description><link>https://hashnode.wallis.dev/using-typescript-project-references-to-share-common-code</link><guid isPermaLink="true">https://hashnode.wallis.dev/using-typescript-project-references-to-share-common-code</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[James Wallis]]></dc:creator><pubDate>Tue, 30 Mar 2021 22:50:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1617144674595/H9I2OV9s1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ever wondered if you can share interfaces, types and functions between TypeScript projects?</p>
<p>I'm currently developing a project consisting of two separate TypeScript applications, one being a React.js dashboard and the other an Azure Function app written in Node.js. As part of the project, the dashboard calls an API in the Azure Function app. This got me thinking, as I'm in control of both the data source and the application that uses the data, is there a way that I can share certain interfaces between the two projects?</p>
<p>The answer is yes, since version 3 of TypeScript you can use <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/project-references.html">Project References</a> to share code between TypeScript projects. When using Project References in my project, however, I couldn't find any official examples on how to use them - hence this post! </p>
<p><em>While the implementation below is what has worked for me, if you have any improvements, let me know in the comments.</em></p>
<h2 id="what-are-project-referenceshttpswwwtypescriptlangorgdocshandbookproject-referenceshtml">What are <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/project-references.html">Project References</a>?</h2>
<blockquote>
<p>Project references allow you to structure your TypeScript programs into smaller pieces. By doing this, you can greatly improve build times, enforce logical separation between components, and organize your code in new and better ways.</p>
</blockquote>
<h2 id="how-to-use">How to use</h2>
<p>Take a project that consists of a frontend and a backend written in TypeScript. Both contain an interface called <code>IData</code> which is exactly the same. Currently, each time I make a change, I have to duplicate it in the other file (which is extremely annoying). </p>
<p>The directory of the project is:</p>
<pre><code>myproject
<span class="hljs-bullet">-</span> frontend
<span class="hljs-bullet">  -</span> app.ts
<span class="hljs-bullet">  -</span> interfaces
<span class="hljs-bullet">    -</span> IData.ts
<span class="hljs-bullet">  -</span> tsconfig.json
<span class="hljs-bullet">-</span> backend
<span class="hljs-bullet">  -</span> server.ts
<span class="hljs-bullet">  -</span> interfaces
<span class="hljs-bullet">    -</span> IData.ts
<span class="hljs-bullet">  -</span> tsconfig.json
</code></pre><p>In order to use a single <code>IData.ts</code> file between both projects, we can use Project References.</p>
<h3 id="adding-the-common-typescript-project">Adding the common TypeScript project</h3>
<p>We will start by creating a third TypeScript project called <code>common</code>, adding an empty <code>tsconfig.json</code> file and copying the <code>IData.ts</code> interface over. We can also remove it from the <code>frontend</code> and <code>backend</code> apps. So the directory structure will be:</p>
<pre><code>myproject
<span class="hljs-bullet">-</span> frontend
<span class="hljs-bullet">  -</span> app.ts
<span class="hljs-bullet">  -</span> tsconfig.json
<span class="hljs-bullet">-</span> backend
<span class="hljs-bullet">  -</span> server.ts
<span class="hljs-bullet">  -</span> tsconfig.json
<span class="hljs-bullet">-</span> common
<span class="hljs-bullet">  -</span> interfaces
<span class="hljs-bullet">    -</span> IData.ts
<span class="hljs-bullet">  -</span> tsconfig.json
</code></pre><p>This isn't enough though. In the <code>common</code> app's <code>tsconfig.json</code> we need to add the following:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"compilerOptions"</span>: {
        <span class="hljs-attr">"target"</span>: <span class="hljs-string">"es5"</span>, <span class="hljs-comment">// Or whatever you want</span>
        <span class="hljs-attr">"module"</span>: <span class="hljs-string">"es2015"</span>, <span class="hljs-comment">// Or whatever you want</span>
        <span class="hljs-attr">"declaration"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">"declarationMap"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"./dist"</span>,
        <span class="hljs-attr">"composite"</span>: <span class="hljs-literal">true</span>
    }
}
</code></pre>
<p>The key parts are:</p>
<ul>
<li><code>declaration</code>: Generates a declaration file that the <code>frontend</code> and <code>backend</code> apps can use to reference items in the <code>common</code> app.</li>
<li><code>composite</code>: Ensures TypeScript can quickly determine where to find the outputs of the referenced project</li>
<li><code>declarationMap</code>: Enables editor features like “Go to Definition” and Rename to transparently navigate and edit code across project boundaries in supported editors</li>
</ul>
<h3 id="referencing-the-common-project-in-frontendbackend">Referencing the common project in <code>frontend</code>/<code>backend</code></h3>
<p>To reference the common <code>IData</code> interface in the <code>frontend</code> and <code>backend</code> apps we need to make a simple change to both of their <code>tsconfig.json</code> files. Add the <code>references</code> property to your existing <code>tsconfig.json</code>.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"compilerOptions"</span>: {
        <span class="hljs-comment">// The usual</span>
    },
    <span class="hljs-attr">"references"</span>: [
        { <span class="hljs-attr">"path"</span>: <span class="hljs-string">"../common"</span> }
    ]
}
</code></pre>
<h3 id="building-the-frontendbackend-apps">Building the <code>frontend</code>/<code>backend</code> apps</h3>
<p>Now that we've added the reference to the common app in order to access its interfaces we need to compile both the <code>frontend</code> and <code>backend</code> apps. </p>
<p>When doing so, ensure you use the <code>--build</code> option so that TypeScript automatically builds all referenced projects.</p>
<pre><code>tsc <span class="hljs-comment">--build .</span>
</code></pre><p>Note: If you're using Next.js with TypeScript, I didn't need to do this. Both <code>next dev</code> and <code>next build</code> kept working just the same.</p>
<h3 id="importing-the-common-interface-into-frontendbackend">Importing the common interface into <code>frontend</code>/<code>backend</code></h3>
<p>This is easier than you might first think, just import <code>IData</code> using its relative path. TypeScript will do the magic when you compile it.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> IData <span class="hljs-keyword">from</span> <span class="hljs-string">'../common/interfaces/IData'</span>
</code></pre>
<h2 id="summary">Summary</h2>
<p>In this post, I've demonstrated how to use TypeScript <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/project-references.html">Project References</a> to use a common project for shared interfaces, functions, types and more!</p>
<p>Feedback on my approach is appreciated! As I said above, I couldn't find an official example to guide me on how to use Project References so any feedback in the comments will help me improve this tutorial and my own TypeScript projects!</p>
<p>Thanks for reading! </p>
]]></content:encoded></item><item><title><![CDATA[Using getStaticProps and getStaticPaths with TypeScript - Next.js]]></title><description><![CDATA[My personal website is built on Next.js and uses both the getStaticProps and getStaticPaths functions to dynamically generate the /blog/ and /portfolio/ pages at build time. While updating both methods to use their proper TypeScript types, following ...]]></description><link>https://hashnode.wallis.dev/using-getstaticprops-and-getstaticpaths-with-typescript-nextjs</link><guid isPermaLink="true">https://hashnode.wallis.dev/using-getstaticprops-and-getstaticpaths-with-typescript-nextjs</guid><dc:creator><![CDATA[James Wallis]]></dc:creator><pubDate>Wed, 24 Mar 2021 21:24:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1616781469480/NgsmLGeHR.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>My <a target="_blank" href="https://wallis.dev">personal website</a> is <a target="_blank" href="https://dev.to/jameswallis/i-completely-rewrote-my-personal-website-using-dev-to-as-a-cms-2pje">built on Next.js</a> and uses both the <code>getStaticProps</code> and <code>getStaticPaths</code> functions to dynamically generate the <code>/blog/</code> and <code>/portfolio/</code> pages at build time. While updating both methods to use their proper TypeScript types, <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops">following the documentation</a>, I ran into an error when reading the parameter that I was passing from <code>getStaticPaths</code> into <code>getStaticProps</code>.</p>
<p>The error that appeared was:</p>
<blockquote>
<p><em>Property 'slug' does not exist on type 'ParsedUrlQuery | undefined'</em></p>
</blockquote>
<p>After doing some research and finding <a target="_blank" href="https://github.com/vercel/next.js/discussions/16522">a discussion on the Next.js GitHub regarding this issue</a>, I recognised it was a gap in their documentation. It explains how to add the type to <code>getStaticProps</code> when used on its own but it doesn't demonstrate how to access the property you've declared in <code>getStaticPaths</code>.</p>
<hr />
<h2 id="background">Background</h2>
<p><a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation"><code>getStaticProps</code></a> and <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation"><code>getStaticPaths</code></a> are two methods that can be used for data fetching in Next.js. Briefly speaking <code>getStaticProps</code> lets you fetch data at build time and <code>getStaticPaths</code> enables you to specify dynamic routes to pre-render pages based on data.</p>
<p>For more information on these functions, read my post on <a target="_blank" href="https://dev.to/jameswallis/different-ways-to-fetch-data-in-next-js-server-side-and-when-to-use-them-1jb0">different ways to fetch data in Next.js</a>.</p>
<hr />
<h2 id="the-error">The error</h2>
<p>Using the example code below I will demonstrate the TypeScript error and advise you on how to fix it. I'm using the variable name <code>slug</code> to create the dynamic routes, but you could use anything - another common name is <code>id</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { GetStaticPaths, GetStaticProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>
<span class="hljs-keyword">import</span> { ParsedUrlQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'querystring'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getStaticPaths: GetStaticPaths = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> arr: <span class="hljs-built_in">string</span>[] = [<span class="hljs-string">'slug1'</span>, <span class="hljs-string">'slug2'</span>]
    <span class="hljs-keyword">const</span> paths = arr.map(<span class="hljs-function">(<span class="hljs-params">slug</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> {
            params: { slug },
        }
    })
    <span class="hljs-keyword">return</span> { paths }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getStaticProps: GetStaticProps = <span class="hljs-keyword">async</span> (context) =&gt; {
    <span class="hljs-comment">// This is where the error occurs</span>
    <span class="hljs-keyword">const</span> { slug } = context.params <span class="hljs-comment">// Property 'slug' does not exist on type 'ParsedUrlQuery | undefined'</span>
    <span class="hljs-keyword">const</span> props = fetch(<span class="hljs-string">`/api/<span class="hljs-subst">${slug}</span>`</span>)
    <span class="hljs-keyword">return</span> { props }
}
</code></pre>
<p>Note the first line of the <code>getStaticProps</code>. Here we are attempting to access the slug variable that was created in <code>getStaticPaths</code> and returned inside the <code>params</code> object. This is the line that causes the error as <code>context.params</code> has the type <code>ParsedUrlQuery | undefined</code> and <code>slug</code> does not exist in <code>ParsedUrlQuery</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> { slug } = context.params
</code></pre>
<hr />
<h2 id="the-fix">The fix</h2>
<p>Fortunately, fixing the issue is as simple as creating an interface that extends <code>ParsedUrlQuery</code> and contains a <code>slug</code> property.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> IParams <span class="hljs-keyword">extends</span> ParsedUrlQuery {
    slug: <span class="hljs-built_in">string</span>
}
</code></pre>
<p>Once we've added that to the file, we need to assert that <code>context.params</code> is of type <code>IParams</code>. This is done like so:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> { slug } = context.params <span class="hljs-keyword">as</span> IParams
</code></pre>
<p>It's as simple as that! Just adding the <code>IParams</code> interface makes the TypeScript error disappear.</p>
<p>Updated example code:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { GetStaticPaths, GetStaticProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>
<span class="hljs-keyword">import</span> { ParsedUrlQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">'querystring'</span>

<span class="hljs-keyword">interface</span> IParams <span class="hljs-keyword">extends</span> ParsedUrlQuery {
    slug: <span class="hljs-built_in">string</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getStaticPaths: GetStaticPaths = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> arr: <span class="hljs-built_in">string</span>[] = [<span class="hljs-string">'slug1'</span>, <span class="hljs-string">'slug2'</span>]
    <span class="hljs-keyword">const</span> paths = arr.map(<span class="hljs-function">(<span class="hljs-params">slug</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> {
            params: { slug },
        }
    })
    <span class="hljs-keyword">return</span> { paths }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getStaticProps: GetStaticProps = <span class="hljs-keyword">async</span> (context) =&gt; {
    <span class="hljs-keyword">const</span> { slug } = context.params <span class="hljs-keyword">as</span> IParams <span class="hljs-comment">// no longer causes error</span>
    <span class="hljs-keyword">const</span> props = fetch(<span class="hljs-string">`/api/<span class="hljs-subst">${slug}</span>`</span>)
    <span class="hljs-keyword">return</span> { props }
}
</code></pre>
<hr />
<h2 id="round-up">Round up</h2>
<p>If this post has helped you use Next.js together with TypeScript, react or let me know in the comments! </p>
<p>Thanks for reading!</p>
<p>Sources:</p>
<ul>
<li><a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching">Data fetching in Next.js (<code>getStaticProps</code> and <code>getStaticPaths</code>)</a></li>
<li><a target="_blank" href="https://github.com/vercel/next.js/discussions/16522">GitHub discussion on the type for <code>context.params.slug</code></a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The easiest way to download SVGs from a website]]></title><description><![CDATA[Usually, when adding icons to a website, I'll use React-icons. There are times, however, when I prefer to copy an exact SVG from a website. Downloading an SVG isn't as easy as downloading an image on a website. Fortunately, SVG Export extracted them ...]]></description><link>https://hashnode.wallis.dev/the-easiest-way-to-download-svgs-from-a-website</link><guid isPermaLink="true">https://hashnode.wallis.dev/the-easiest-way-to-download-svgs-from-a-website</guid><category><![CDATA[Web Development]]></category><category><![CDATA[SVG]]></category><category><![CDATA[webdev]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[James Wallis]]></dc:creator><pubDate>Tue, 23 Mar 2021 01:37:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1616464317201/jhCZJt31r.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Usually, when adding icons to a website, I'll use <a target="_blank" href="https://react-icons.github.io/react-icons/">React-icons</a>. There are times, however, when I prefer to copy an exact SVG from a website. Downloading an SVG isn't as easy as downloading an image on a website. Fortunately, <a target="_blank" href="https://svgexport.io/">SVG Export</a> extracted them for me.</p>
<h2 id="the-problem">The problem</h2>
<p>Ever use inspect element to download images that you're unable to right-click and either save or open? You'll likely have noticed that with SVGs it's not easy to download them from a website. - or at least I did when I was attempting to "clone" a webpage for my latest project.</p>
<p>Take the Dev.to logo that is visible on every page on Dev.to, for example. Right-clicking on that doesn't give you the option to open it in a new tab or save it. Moreover, using inspect element only gives you the potentially huge SVG tags, leaving you to work out where the SVG begins and ends in the page's HTML.</p>

  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g6h4aowgsjcg73hzounl.png" />
  Unable to open or download the Dev.to icon SVG!


<h2 id="the-solution">The solution</h2>
<p>Enter <a target="_blank" href="https://svgexport.io/">SVG Export</a>, by <a target="_blank" href="https://twitter.com/delanebob">Stephen Delaney</a> is a browser extension <a target="_blank" href="https://chrome.google.com/webstore/detail/svg-export/naeaaedieihlkmdajjefioajbbdbdjgp/related?hl=en-GB">for Google Chrome</a> and <a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/svg-export">Mozilla Firefox</a>. </p>
<p>After installing the extension, clicking the extension icon will trigger SVG Export to extract all available SVGs on your current webpage, ensuring their inline styles are kept. A new tab will be opened displaying all SVGs. From there, locate the SVGs you're after and either download the SVG as a file, copy it as code or paste it into your favourite design tools like Sketch, Figma or Framer.</p>
<p>To download the Dev.to icon mentioned above, I simply opened any page on <a target="_blank" href="https://dev.to">Dev.to</a>, clicked the SVG Export icon to start the extract, and finally located it in the newly opened tab.</p>

  <img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h3lzqjvt09is8niudrsz.gif" />
  SVG Export in action


<h2 id="round-up">Round up</h2>
<p>In this post, I demonstrated how I was able to easily extract an SVG from a webpage using <a target="_blank" href="https://svgexport.io/">SVG Export</a>.</p>
<p>Do you have another, perhaps better, method of downloading SVGs from websites? If you do, let me know in the comments 👇</p>
<p>If this article has helped, drop a reaction!</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[I completely rewrote my personal website using Dev.to as a CMS]]></title><description><![CDATA[The final weekend of January 2021 was uneventful in comparison with other years - in the UK we were in full lockdown due to the Coronavirus. It was, however, the perfect opportunity to completely rewrite my personal website.
Why?
I decided to redesig...]]></description><link>https://hashnode.wallis.dev/rewriting-my-website-with-a-devto-cms</link><guid isPermaLink="true">https://hashnode.wallis.dev/rewriting-my-website-with-a-devto-cms</guid><category><![CDATA[DEVCommunity]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[James Wallis]]></dc:creator><pubDate>Wed, 03 Feb 2021 19:40:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615815890185/N3QzV6j9T.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The final weekend of January 2021 was uneventful in comparison with other years - in the UK we were in full lockdown due to the Coronavirus. It was, however, the perfect opportunity to completely rewrite <a target="_blank" href="https://wallis.dev">my personal website</a>.</p>
<h2 id="why">Why?</h2>
<p>I decided to redesign and rewrite my website for several reasons:</p>
<ul>
<li>I wanted to move from JavaScript to TypeScript.</li>
<li>The website was styled using <a target="_blank" href="https://github.com/vercel/styled-jsx">styled-jsx</a>, which can be a pain to maintain and IMO is a bit messy. At the moment I'm using Tailwind CSS and so far loving it and its utility-style nature; I wanted my personal website to reflect this.</li>
<li>I no longer liked the design and wanted it to be cleaner and simpler.</li>
<li>I wanted my blog and portfolio to be loaded dynamically from a CMS rather than having to copy+paste a new page for each entry - See the <em><code>Originally published at wallis.dev</code></em> at the top of this article.</li>
</ul>

  <img src="https://dev-to-uploads.s3.amazonaws.com/i/rs1bvowr8eyxg3gly85p.png" alt="The old wallis.dev home page" />
  My old home page


<h2 id="technologies-used">Technologies used</h2>
<ul>
<li><strong><a target="_blank" href="https://www.typescriptlang.org">TypeScript</a></strong> - Since being introduced to TypeScript at work, I've started to understand the benefits over plain JavaScript.</li>
<li><strong><a target="_blank" href="https://nextjs.org">Next.js</a></strong> - I don't try to hide the fact that I love Next.js, it's so simple to use and to date <a target="_blank" href="https://dev.to/jameswallis">most of my articles contain Next.js in some way</a>.</li>
<li><strong><a target="_blank" href="https://tailwindcss.com">Tailwind CSS</a></strong> - Lately I've been using Tailwind CSS heavily. To quote their homepage, it enables me to "rapidly build modern websites without ever leaving [my React component]". Tailwind CSS also made it incredibly easy to add a <a target="_blank" href="https://tailwindcss.com/docs/dark-mode">dark mode</a>. Also <a target="_blank" href="https://github.com/tailwindlabs/tailwindcss-typography">Tailwind Typography</a>.</li>
<li><strong><a target="_blank" href="https://docs.dev.to/api">Dev.to API</a></strong> to dynamically build the blog and portfolio pages ⬅️  <em>My favourite feature</em>.</li>
</ul>
<h2 id="using-devto-as-a-cms">Using Dev.to as a CMS</h2>
<p>My favourite part of my website is the use of Dev.to as a Content Management System for the blog and portfolio pages. I've seen the Dev.to API utilised before to display a user's articles on their website but, AFAIK, not quite in the same way as I've applied it.</p>
<p>The benefits of using Dev.to as a CMS are:</p>
<ol>
<li>Dev.to stores the articles and any images that are uploaded and used.</li>
<li>I can use Dev.to's editor and the ability to draft an article and publish it later.</li>
<li>Has a built-in RSS feed that I can use to share my articles to other sites such as <a target="_blank" href="https://community.codenewbie.org/">CodeNewbie</a> and Medium.</li>
<li>Although Dev.to has the article first, the use of a canonical URL ensures that Google and other sites see my personal website as the source site for the articles.</li>
<li>Converts the article into HTML for me. <em>I ended up rendering the HTML from the article markdown myself, as it required fewer requests to the Dev.to API.</em></li>
</ol>
<h3 id="disclaimer">Disclaimer</h3>
<p>Before I continue I want to <em>stress</em> that I intend to use Dev.to purely for my blog and portfolio (past projects / <code>showdev</code>). <strong>I won't be using Dev.to to create pages which are not articles</strong> and would cause Dev.to to become cluttered with spam if others follow suit. For example, the about section on the home page is hardcoded into the website and if I created a page for my education history, I'd keep that purely for the website and wouldn't post it to Dev.to - <a target="_blank" href="https://dev.to/jameswallis/combining-markdown-and-dynamic-routes-to-make-a-more-maintainable-next-js-website-3ogl">I'd probably use Markdown for these</a>.</p>
<h3 id="how-it-works">How it works</h3>
<p><a target="_blank" href="https://github.com/james-wallis/wallis.dev">View the code on GitHub</a></p>
<p>Built using Next.js, the website uses two dynamic routing functions (<a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation"><code>getStaticPaths</code></a> and <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation"><code>getStaticProps</code></a>) to generate the <a target="_blank" href="https://github.com/james-wallis/wallis.dev/blob/master/pages/blog/%5Bslug%5D.tsx">blog</a> and <a target="_blank" href="https://github.com/james-wallis/wallis.dev/blob/master/pages/portfolio/%5Bslug%5D.tsx">portfolio</a> pages.</p>
<p>Before an article is displayed on my website, it must meet the two following requirements:</p>
<ol>
<li>Must be published (obviously)</li>
<li>Must have a canonical URL directing to my website. This enables me to pick which articles are displayed, what the article's path will be on my website (not the post ID). Moreover, an article with a canonical URL pointing to <code>https://wallis.dev/blog/...</code> will be built as part of my blog whereas, if its canonical URL is <code>https://wallis.dev/portfolio/...</code> it will be a portfolio piece.</li>
</ol>
<p>For every article that meets the requirements, the subsequent build process is followed:</p>
<ol>
<li><p>At build time, Next.js calls the <code>getStaticPaths</code> function which </p>
<ol>
<li>Fetches a list of my published articles using the Dev.to API (<a target="_blank" href="https://docs.dev.to/api/#operation/getUserArticles"><code>/api/articles/me</code></a>).</li>
<li>Converts the article's markdown to HTML.</li>
<li>Saves the articles to a cache file for use in the next step. </li>
<li>A dynamic page is created within the Next.js context for each article - the page <code>slug</code> will be the canonical URL path.</li>
</ol>
</li>
<li><p>For each page, Next.js calls <code>getStaticProps</code> which fetches the page's article from the cache. The article contains the name, description and HTML. </p>
<ul>
<li><em>I also attempted making another API request to the Dev.to API (<a target="_blank" href="https://docs.dev.to/api/#operation/getArticleById"><code>/api/articles/{id}</code></a>) to fetch the page's article, so I could use the HTML rendered by Dev.to. However, this caused build failures as I was making too many API requests at once - so now I render the markdown using <a target="_blank" href="https://github.com/remarkjs/remark-html"><code>remark-html</code></a>.</em></li>
</ul>
</li>
<li>Finally, the page is rendered. I use custom elements to display the article <code>name</code> and <code>description</code> and then display the HTML I <a target="_blank" href="https://github.com/james-wallis/wallis.dev/blob/master/lib/markdown.ts#L19-L29">rendered earlier in <code>getStaticPaths</code> using <code>remark-html</code></a>. For styling, I use the <a target="_blank" href="https://github.com/tailwindlabs/tailwindcss-typography">Tailwind Typography plugin</a>.</li>
</ol>
<p>To ensure that the website is always in sync with my articles on Dev.to, I use a <a target="_blank" href="https://vercel.com/docs/more/deploy-hooks">Vercel Deploy hook</a> which is triggered each time I create or update an article using a <a target="_blank" href="https://docs.dev.to/api/#tag/webhooks">Dev.to webhook</a>. I use a Deploy Hook rather than <a target="_blank" href="https://nextjs.org/blog/next-9-5#stable-incremental-static-regeneration">Incremental Static Regeneration</a> so that the blog is only rebuilt when something has changed rather than at random intervals.</p>
<p><em>Note: I use Dev.to APIs that require authorisation as they seem to have a higher request limit compared to the public routes. When using public APIs and fetching each article via the article API, I found that my builds were failing with a <code>429</code> error which is Dev.to rate-limiting requests. - I probably could switch to using public APIs now that I'm using a cache to read the articles from.</em></p>
<p><strong>I'm currently writing a detailed article which describes in greater detail how my website utilises Dev.to as a CMS, stay tuned (and follow on Dev.to to be notified when I release it)!</strong></p>
<h3 id="how-it-looks">How it looks</h3>
<p><a target="_blank" href="https://wallis.dev">Visit wallis.dev</a></p>

  <img src="https://dev-to-uploads.s3.amazonaws.com/i/7apbs7bdr6o1fwiouwc5.gif" alt="Navigating through my website" />
  Navigating through wallis.dev


<h3 id="future-improvements">Future improvements</h3>
<ol>
<li>Add syntax highlighting to code blocks like on Dev.to. <a target="_blank" href="https://github.com/james-wallis/wallis.dev/blob/master/lib/markdown.ts#L24">Completed using <code>highlight.js</code> and <code>remark-highlight.js</code></a>.</li>
<li>Add a <a target="_blank" href="https://dev.to/jameswallis/using-emailjs-with-captcha-verification-in-a-next-js-application-1g1g">contact form using EmailJS</a>.</li>
<li>Only rebuild the website if the content of an article has changed or one is created - reduces the website being needlessly redeployed.</li>
</ol>
<h2 id="summary">Summary</h2>
<p>In this article, I discussed rewriting my personal website from the ground up using Dev.to as a Content Management System for the blog and portfolio pages.</p>
<p>Like the idea of using Dev.to as a CMS for your blog? React! Found something I could improve or that you would have done differently? Let me know in the comments.</p>
<p>Thanks for reading!</p>
<p><strong><a target="_blank" href="https://wallis.dev/blog/rewriting-my-website-with-a-devto-cms">By the way, you can view this article live on my website here.</a></strong></p>
]]></content:encoded></item><item><title><![CDATA[5 built-in Next.js features you absolutely should check out]]></title><description><![CDATA[Introduction
Recently I've been using Next.js both at work and on after-work projects. Next.js is React framework that enables functionality such as server-side rendering and generating static websites. It has become my go-to technology when I'm buil...]]></description><link>https://hashnode.wallis.dev/5-built-in-nextjs-features-you-absolutely-should-check-out</link><guid isPermaLink="true">https://hashnode.wallis.dev/5-built-in-nextjs-features-you-absolutely-should-check-out</guid><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[James Wallis]]></dc:creator><pubDate>Tue, 26 Jan 2021 00:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615813488855/DK_GYwUV-.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>Recently I've been using <a target="_blank" href="https://nextjs.org/">Next.js</a> both at work and on after-work projects. Next.js is React framework that enables functionality such as server-side rendering and generating static websites. It has become my go-to technology when I'm building a frontend application, overtaking plain old React. </p>
<p>With Next.js you get many things out of the box such as built-in routing, automatic code splitting and it will even decide whether your app can be statically rendered or needs to be rendered server-side on every request - all of this without any configuration. In fact, when creating a new React application I default to using <a target="_blank" href="https://nextjs.org/docs/api-reference/create-next-app"><code>create-next-app</code></a> as opposed to <a target="_blank" href="https://github.com/facebook/create-react-app"><code>create-react-app</code></a>.</p>
<p>During my time developing with Next.js I discovered a few features which are easy to miss when you're just getting started. Some of these features helped me solve some problems I was having with my application. </p>
<h2 id="features-you-should-check-out">Features you should check out 🤩</h2>
<ol>
<li><p><strong>Export your Next.js application into <a target="_blank" href="https://nextjs.org/docs/advanced-features/static-html-export">static HTML using <code>next export</code></a></strong>. </p>
<ul>
<li>Giving you the ability to run it without a running Node.js server while still being able to make data-fetching requests at build time using <code>getStaticProps</code>. This feature bridges the gap between Next.js and its longtime alternative <a target="_blank" href="https://www.gatsbyjs.com/">Gatsby</a> which is exclusively a static site generator.</li>
<li><em>I've used <code>next export</code> to host a Next.js site on GitHub Pages (although with issues that were solved with the subsequent feature).</em></li>
</ul>
</li>
<li><p><strong>The <code>next.config.js</code> <a target="_blank" href="https://nextjs.org/docs/api-reference/next.config.js/cdn-support-with-asset-prefix"><code>assetPrefix</code></a> and <a target="_blank" href="https://nextjs.org/docs/api-reference/next.config.js/basepath"><code>basePath</code></a> options</strong>. </p>
<ul>
<li>On its own <code>assetPrefix</code> allows you to prefix all URLs to assets, like images, so that you can use assets hosted on a CDN. While <code>basePath</code> provides the ability to host the app on the subpath of a domain such as <code>https://domain.com/app-on-this-subpath</code>.</li>
<li><em><a target="_blank" href="https://dev.to/jameswallis/next-js-basepath-and-why-its-awesome-for-github-pages-and-static-sites-41ba">In the past I've utilised <code>next export</code>, <code>assetPrefix</code> and <code>basePath</code> together to host a Next.js app on GitHub Pages</a> - without <code>assetPrefix</code> and <code>basePath</code> you are unable to host a Next.js project on GitHub pages (without a custom domain) as it puts them on a subpath.</em></li>
</ul>
</li>
<li><p><strong><a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration">Incremental Static Regeneration</a></strong>. </p>
<ul>
<li>A feature of <code>getStaticProps</code> which allows you to regenerate a static page while your app is running. It works by triggering a page rebuild in the background, which fetches updated page data, and replaces the existing HTML page with the newly generated one once the build has completed.</li>
<li><em>I haven't tried this feature but will in the future as it is a better alternative to completely rebuilding a static Next.js application each time data that it relies on changes.</em></li>
</ul>
</li>
<li><p><strong><a target="_blank" href="https://nextjs.org/docs/advanced-features/i18n-routing">Internationalized (i18n) routing</a></strong>. </p>
<ul>
<li>If you're building a website that will be available in different countries, this feature is a game-changer. It makes supporting multiple languages simpler by enabling you to provide a list of supported locales which Next.js can read and automatically set up routing to ensure that users see the correct locale for their country. You can assign a default locale that will be used when no matching locale is detected for a user. Next.js supports both domain routing (<code>example.com</code>, <code>example.fr</code>) and subpath routing (<code>example.com/en</code>, <code>example.com/fr</code>) meaning it doesn't restrict how you plan to host your application.</li>
<li><em>If I ever decide to make my website multi-lingual or work on a global project, this is a feature I will definitely be using.</em></li>
</ul>
</li>
<li><p><strong><a target="_blank" href="https://nextjs.org/docs/advanced-features/measuring-performance">Measuring Performance - <code>reportWebVitals</code></a></strong>.</p>
<ul>
<li>Next.js contains a built-in relayer allowing you to analyse and measure the performance of your application. To activate this you use the built-in function <code>reportWebVitals</code>. Next.js calls <code>reportWebVitals</code> with a single <code>metrics</code> parameter, an object containing various properties such as an <code>id</code>, the <code>startTime</code> of a metric and a <code>value</code> which can be the duration of a metric. This function will be called when running on the client-side. In development, you can simply log out the values to easily measure the performance of your application. In production, however, <a target="_blank" href="https://nextjs.org/docs/advanced-features/measuring-performance#sending-results-to-analytics">you can use this function to send the <code>metrics</code> to your own analytical service</a>. They supply an example of this for use with Google Analytics.</li>
<li><em>I haven't used <code>reportWebVitals</code> but in the future I'll add it to <a target="_blank" href="https://dev.to/jameswallis/adding-google-analytics-to-any-next-js-app-46h1">my Google Analytics article</a>. I use <code>reportWebVitals</code> on <a target="_blank" href="https://dev.to/jameswallis/i-completely-rewrote-my-personal-website-using-dev-to-as-a-cms-2pje">my personal website</a>.</em></li>
<li>Using the following function should provide more accurate metrics than plain Google Analytics usage:<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reportWebVitals</span>(<span class="hljs-params">{ id, name, label, value }</span>) </span>{
<span class="hljs-comment">// Use `window.gtag` if you initialized Google Analytics as this example:</span>
<span class="hljs-comment">// https://github.com/vercel/next.js/blob/canary/examples/with-google-analytics/pages/_document.js</span>
<span class="hljs-built_in">window</span>.gtag(<span class="hljs-string">'event'</span>, name, {
<span class="hljs-attr">event_category</span>:
label === <span class="hljs-string">'web-vital'</span> ? <span class="hljs-string">'Web Vitals'</span> : <span class="hljs-string">'Next.js custom metric'</span>,
<span class="hljs-attr">value</span>: <span class="hljs-built_in">Math</span>.round(name === <span class="hljs-string">'CLS'</span> ? value * <span class="hljs-number">1000</span> : value), <span class="hljs-comment">// values must be integers</span>
<span class="hljs-attr">event_label</span>: id, <span class="hljs-comment">// id unique to current page load</span>
<span class="hljs-attr">non_interaction</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// avoids affecting bounce rate.</span>
})
}
</code></pre>
</li>
</ul>
</li>
</ol>
<h2 id="bonus">Bonus</h2>
<ul>
<li><p><strong>The incredible amount of examples available in the Next.js GitHub repository</strong>.</p>
<ul>
<li><p>If you haven't already stumbled onto them, the <a target="_blank" href="https://github.com/vercel/next.js/tree/canary/examples">Next.js GitHub repository contains an examples directory</a> that is full of examples. These show you how to use technologies such as <a target="_blank" href="https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss">Tailwind CSS</a>, <a target="_blank" href="https://github.com/vercel/next.js/tree/canary/examples/with-typescript">TypeScript</a> and various CMSs such as <a target="_blank" href="https://github.com/vercel/next.js/tree/canary/examples/cms-contentful">Contentful</a> with Next.js. You can use <code>create-next-app</code> to download an example.</p>
</li>
<li><p><em>When I am incorporating new technology into an existing Next.js application, the example directory is the first place I check for guidance on how to integrate it.</em></p>
</li>
</ul>
</li>
</ul>
<h2 id="final-words">Final words</h2>
<p>These are just a few of the features that Next.js includes that can automatically improve your application without having to install any external dependencies.</p>
<p>If you liked this article, hit the like button. Something I can do better? Leave a comment!</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Quickly adding Google Analytics to Next.js with FAQs]]></title><description><![CDATA[I use Google Analytics to track how many users are visiting websites that I develop on a weekly basis, how long they stay on the site and what are the most popular pages.
It's easy to add to your site and in minutes you'll go from having no clue how ...]]></description><link>https://hashnode.wallis.dev/quickly-adding-google-analytics-to-nextjs-with-faqs</link><guid isPermaLink="true">https://hashnode.wallis.dev/quickly-adding-google-analytics-to-nextjs-with-faqs</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[analytics]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[James Wallis]]></dc:creator><pubDate>Wed, 30 Dec 2020 14:44:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1616071150896/d1TsF8lfT.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I use Google Analytics to track how many users are visiting websites that I develop on a weekly basis, how long they stay on the site and what are the most popular pages.</p>
<p>It's easy to add to your site and in minutes you'll go from having no clue how people are using your website to being able to record and analyse every detail on every interaction a user has with your website.</p>
<p>I'll split this post up into 3 parts:</p>
<ol>
<li>What we're doing</li>
<li>How to add Google Analytics to Next.js</li>
<li>FAQs - A lot of the Google Analytics tutorials don't explain why you should do things a certain way. For example, why are we using this Next.js <code>_document.js</code> file and what does it do? or how to I get a <code>GA_MEASUREMENT_ID</code>?</li>
</ol>
<p><strong>Key takeaway</strong> if you're glancing at this article:</p>
<blockquote>
<p><em>Adding Google Analytics to a Next.js project is easy and you can copy and paste the code below. I wouldn't use an external NPM module as you're just adding another dependency that can become outdated in the future.</em></p>
</blockquote>
<h2 id="why-add-google-analytics">Why add Google Analytics</h2>
<blockquote>
<p>Google Analytics is a web analytics service offered by Google that tracks and reports website traffic.</p>
</blockquote>
<p>I've recently begun adding Google Analytics to every website that I've developed including <a target="_blank" href="https://dev.to/jameswallis/modernising-an-existing-bootstrap-website-using-next-js-and-tailwind-css-c2g">wallisconsultancy.co.uk which I recently developed alongside a series of tutorial blog posts</a>. Google Analytics is great at tracking website usage and more, although I currently use it purely to monitor user count, most popular pages and page performance which is all reported on the Google Analytics dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1616070910258/-QMtGXIc0.png" alt="Alt Text" />
<em>A Google Analytics dashboard</em></p>
<p>In addition to the above, it's <strong>free</strong> and takes minutes to set up with a Next.js project (if you follow this post 😉).</p>
<h2 id="adding-google-analytics-to-a-nextjs-project">Adding Google Analytics to a Next.js project</h2>
<h3 id="prerequisites">Prerequisites</h3>
<ol>
<li>A Next.js project hosted on a domain that can be used with Google Analytics preferably with at least one page. - <a target="_blank" href="https://vercel.com/solutions/nextjs">Vercel (creators of Next.js) is the easiest place to host your website</a>.</li>
<li>A Google Analytics account with a created property and the ID (known as a <code>GA_MEASUREMENT_ID</code>) that is given to you and is used to identify your website with Google. Keep the <code>GA_MEASUREMENT_ID</code> handy, you'll need it. - <em>I've covered this in the FAQs if you haven't used Google Analytics before</em>.</li>
</ol>
<h3 id="addmodify-the-documentjs-file-in-your-nextjs-project">Add/modify the <code>_document.js</code> file in your Next.js project</h3>
<p>Create a <a target="_blank" href="https://nextjs.org/docs/advanced-features/custom-document">custom <code>_document.js</code></a> file in your <code>pages</code> directory and add the following code:
<em>If you're using TypeScript, check out this <a target="_blank" href="https://github.com/james-wallis/TheVECentre/blob/main/pages/_document.tsx">custom <code>_document.tsx</code> on GitHub</a> instead.</em></p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> Document, {
  Html, Head, Main, NextScript,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'next/document'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyDocument</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Document</span> </span>{
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">async</span> getInitialProps(ctx) {
    <span class="hljs-keyword">const</span> initialProps = <span class="hljs-keyword">await</span> Document.getInitialProps(ctx);
    <span class="hljs-keyword">return</span> { ...initialProps };
  }

  render() {
    <span class="hljs-keyword">const</span> GA_MEASUREMENT_ID = <span class="hljs-string">'GA_MEASUREMENT_ID'</span>; <span class="hljs-comment">// Paste your GTAG here</span>
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Head</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">script</span>
            <span class="hljs-attr">async</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">https:</span>//<span class="hljs-attr">www.googletagmanager.com</span>/<span class="hljs-attr">gtag</span>/<span class="hljs-attr">js</span>?<span class="hljs-attr">id</span>=<span class="hljs-string">${GA_MEASUREMENT_ID}</span>`}
          /&gt;</span><span class="xml">
          <span class="hljs-tag">&lt;<span class="hljs-name">script</span>
            // <span class="hljs-attr">eslint-disable-next-line</span> <span class="hljs-attr">react</span>/<span class="hljs-attr">no-danger</span>
            <span class="hljs-attr">dangerouslySetInnerHTML</span>=<span class="hljs-string">{{</span>
              <span class="hljs-attr">__html:</span> `
                <span class="hljs-attr">window.dataLayer</span> = <span class="hljs-string">window.dataLayer</span> || [];
                <span class="hljs-attr">function</span> <span class="hljs-attr">gtag</span>(){<span class="hljs-attr">dataLayer.push</span>(<span class="hljs-attr">arguments</span>);}
                <span class="hljs-attr">gtag</span>('<span class="hljs-attr">js</span>', <span class="hljs-attr">new</span> <span class="hljs-attr">Date</span>());
                <span class="hljs-attr">gtag</span>('<span class="hljs-attr">config</span>', '${<span class="hljs-attr">GA_MEASUREMENT_ID</span>}', {
                  <span class="hljs-attr">page_path:</span> <span class="hljs-attr">window.location.pathname</span>,
                });
              `,
            }}
          /&gt;</span><span class="xml">
        <span class="hljs-tag">&lt;/<span class="hljs-name">Head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">body</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">NextScript</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Html</span>&gt;</span></span></span></span>
    );
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MyDocument;
</code></pre>
<p>If you've already got a custom <code>_document.js</code> then the key parts are within the <code>Head</code> component (remember to add the <code>GA_MEASUREMENT_ID</code> variable):</p>
<pre><code class="lang-jsx">&lt;Head&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>
    <span class="hljs-attr">async</span>
    <span class="hljs-attr">src</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">https:</span>//<span class="hljs-attr">www.googletagmanager.com</span>/<span class="hljs-attr">gtag</span>/<span class="hljs-attr">js</span>?<span class="hljs-attr">id</span>=<span class="hljs-string">${GA_MEASUREMENT_ID}</span>`}
  /&gt;</span></span>
  <span class="xml"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>
    // <span class="hljs-attr">eslint-disable-next-line</span> <span class="hljs-attr">react</span>/<span class="hljs-attr">no-danger</span>
    <span class="hljs-attr">dangerouslySetInnerHTML</span>=<span class="hljs-string">{{</span>
      <span class="hljs-attr">__html:</span> `
        <span class="hljs-attr">window.dataLayer</span> = <span class="hljs-string">window.dataLayer</span> || [];
        <span class="hljs-attr">function</span> <span class="hljs-attr">gtag</span>(){<span class="hljs-attr">dataLayer.push</span>(<span class="hljs-attr">arguments</span>);}
        <span class="hljs-attr">gtag</span>('<span class="hljs-attr">js</span>', <span class="hljs-attr">new</span> <span class="hljs-attr">Date</span>());
        <span class="hljs-attr">gtag</span>('<span class="hljs-attr">config</span>', '${<span class="hljs-attr">GA_MEASUREMENT_ID</span>}', {
          <span class="hljs-attr">page_path:</span> <span class="hljs-attr">window.location.pathname</span>,
        });
      `,
    }}
  /&gt;</span></span></span>
&lt;/Head&gt;
</code></pre>
<p>Explanation:</p>
<ul>
<li>In the first <code>script</code> tag the Google Analytics JavaScript file is loaded into the browser.</li>
<li>In the second <code>script</code> tag the Google Analytics code is initialised and tracking is started on the current page.</li>
</ul>
<p>Once you've added your <code>GA_MEASUREMENT_ID</code> and pushed your changes into a live environment such as Vercel, you should see data appearing on your Google Analytics dashboard - if you don't, try visiting your website!</p>
<p>And that is all it takes to add Google Analytics to your Next.js application - told you it was easy! </p>
<h2 id="faqs">FAQs</h2>
<h3 id="what-does-the-code-above-load-onto-my-website">What does the code above load onto my website?</h3>
<p>The <code>gtag.js</code> is a script which allows you to send event data to Google, in this case it's used for Google Analytics. Read more about it and the <code>GA_MEASUREMENT_ID</code> at <a target="_blank" href="https://developers.google.com/analytics/devguides/collection/gtagjs">Google Developers - <code>gtag.js</code></a>.</p>
<h3 id="what-is-a-gameasurementid-and-how-do-i-get-one">What is a <code>GA_MEASUREMENT_ID</code> and how do I get one?</h3>
<p>The <code>GA_MEASUREMENT_ID</code> is the ID of the Google Analytics property that you want to collect data for. To get a <code>GA_MEASUREMENT_ID</code> you need to create a new property on Google Analytics - you can follow this <a target="_blank" href="https://support.google.com/analytics/answer/10269537">Google Support article to learn how to sign up to Google Analytics and create a property</a>.</p>
<h3 id="what-are-the-documentjs-and-appjs-files-in-a-nextjs-project">What are the <code>_document.js</code> and <code>_app.js</code> files in a Next.js project?</h3>
<p>The Next.js specific <code>_document.js</code> and <code>_app.js</code> are two special files that live in the <code>pages</code> directory but are not rendered as pages by Next.js (you can't reach <code>/_document</code> in your browser).</p>
<p><em>Note: The <code>Head</code> component used in <code>_document.js</code>'s <code>Head</code> different to <code>next/head</code> and should be used for common code on every page.</em></p>
<p><code>_document.js</code> is rendered server-side only and controls the <code>&lt;html&gt;</code> and <code>&lt;body&gt;</code> tags of HTML. It can be used to add custom elements into your <code>&lt;html&gt;</code> tag that will be the same on every page such as the Google Analytics code or a favicon.</p>
<p><code>_app.js</code> is rendered client-side, potentially on every page change. It's essentially a wrapper around each Next.js page that you have. It can be used to maintain a consistent layout on each page, add a custom CSS stylesheet or persist state on a page change.</p>
<p>You can read more about these on the Next.js website:</p>
<ul>
<li><a target="_blank" href="https://nextjs.org/docs/advanced-features/custom-document">Custom <code>_document.js</code></a></li>
<li><a target="_blank" href="https://nextjs.org/docs/advanced-features/custom-app">Custom <code>_app.js</code></a></li>
</ul>
<h3 id="why-use-the-documentjs-file-over-appjs">Why use the <code>_document.js</code> file over <code>_app.js</code>?</h3>
<p>The <code>Head</code> component (from <code>next/document</code>) that is used in <code>_document.js</code> is rendered serverside whereas the <code>Head</code> component (from <code>next/head</code>) in <code>_app.js</code> will update on each page change meaning that the Google Analytics script may be reloaded which could slow down the site.</p>
<h2 id="round-up">Round up</h2>
<p>If anything I've said has helped you add Google Analytics to your site, give me a reaction.</p>
<p>Any more questions? Let me know in the comments!</p>
<p>Thanks for reading the article!</p>
]]></content:encoded></item><item><title><![CDATA[Modernising an existing Bootstrap website using Next.js and Tailwind CSS]]></title><description><![CDATA[This blog is part of a series where I document rebuilding a website that relies on HTML, CSS and Bootstrap in React.js using the Next.js framework to improve performance, reduce costs and increase my workflow for future changes.
The finished website:...]]></description><link>https://hashnode.wallis.dev/modernising-an-existing-bootstrap-website-using-nextjs-and-tailwind-css</link><guid isPermaLink="true">https://hashnode.wallis.dev/modernising-an-existing-bootstrap-website-using-nextjs-and-tailwind-css</guid><category><![CDATA[Next.js]]></category><category><![CDATA[React]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[James Wallis]]></dc:creator><pubDate>Sun, 26 Jul 2020 22:05:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1615939594835/J0zO_uFvu.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>This blog is part of a series where I document rebuilding a website that relies on HTML, CSS and Bootstrap in React.js using the Next.js framework to improve performance, reduce costs and increase my workflow for future changes.</em></p>
<p>The finished website: https://wallisconsultancy.co.uk
The source code: https://github.com/james-wallis/wallisconsultancy</p>
<h1 id="motivation">Motivation</h1>
<p>Wallisconsultancy.co.uk is a website that I developed in 2016, just after I finished my first year at the University of Portsmouth. One of my modules was a Web Fundamentals course which explained the basics of HTML &amp; CSS with a sprinkling of JavaScript at the end. I wanted a summer project with the aim that I would be a much better HTML and CSS developer next year when I was asked to build a web-based dashboard as a part of the Web Programming coursework.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615939591332/9Fwe7ULAn.jpeg" alt="Original Wallis Consultancy website" />
<em>The original Wallis Consultancy website</em></p>
<p>The original website was written using in 2011 in HTML and CSS using a PHP backend. As of 2016 the design was outdated and was not responsive rendering it unusable for anyone on a device with a small screen. Over the summer I rewrote the site using HTML, CSS and the Bootstrap framework and gave it a PHP backend so that I could reuse certain aspects of the page and make a general layout. Given that it was my first professional website, I was pretty happy with the result. In 2018 I moved it from a GoDaddy server to a Docker container to make it cheaper to run and easier for me to maintain.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1615939593433/lvyGCNvNW.jpeg" alt="Current Wallis Consultancy website" />
<em>The current Wallis Consultancy website</em></p>
<p>It’s now 2020 and I have been requested to make changes to the website. While it can be said that it’s working perfectly in its current condition, adding new pages and subtle features are difficult due to its reliance on HTML. Additionally, my skillset has improved know that this site will benefit from my experience using Next.js, a React.js framework that specialises in building server rendered, SEO friendly sites. In addition, as uses a Bootstrap Template, it came with a lot of CSS and JavaScript that is unused and in no way optimised. Next.js does a nice job of minifying the JavaScript and Tailwind CSS, a CSS framework, will help to reduce the amount of CSS I need to create and remove anything that is unused.</p>
<h2 id="why-nextjs">Why Next.js</h2>
<p>When a website is built using React.js the clients browser is required to run JavaScript and construct the page itself, this is bad for two reasons. </p>
<ol>
<li>There might be a few seconds before the website can be used by the client.  - Bad user experience.</li>
<li>For SEO - although search engines are being improved to handle JavaScript applications there is no guarantee that the search bot will render the application correctly. For this reason it is still preferred to send static HTML to the client.</li>
</ol>
<p>Next.js solves these problems by statically generating and server-rendering a React application meaning we can send rendered HTML to the client as opposed to large JavaScript files. </p>
<p>Next.js has other noteworthy features such as:</p>
<ul>
<li>Automatic Code Splitting - Rendered pages only contain the JavaScript they need, rather than a single JavaScript file for all pages.</li>
<li>Built in Routing - To create a new page, just add a file in the <code>pages</code> folder</li>
<li>Prefetching - Using the <code>Link</code> component we can tell Next what pages the user is able to visit, Next responds by pre-fetching the content required for the page in the background so there is no load times between pages.</li>
<li>A Next app can be exported to static HTML - This is a huge feature that was released in <a target="_blank" href="https://nextjs.org/blog/next-9-3#next-gen-static-site-generation-ssg-support">Next.js 9.3</a> websites to be run through Github pages.</li>
</ul>
<p><a target="_blank" href="https://nextjs.org/">Next.js</a></p>
<h2 id="why-tailwind-css">Why Tailwind CSS</h2>
<p>Tailwind CSS describes itself as a utility-first CSS framework for rapidly building custom designs. It works by having an existing library of CSS classes that you can add to a React.js component using the <code>className</code> prop. To add a height of 100% you add the class <code>h-full</code> to the component, this is an improvement to giving a component a classname and then opening a CSS file to define what you want the class to do - it makes styling a component a fast process.</p>
<p>For responsive sites, there exists the intuitive <code>{screen}:</code> prefix which makes it easy to control responsive classes, for a desktop all that is the <code>lg</code> prefix next to the styling e.g. <code>lg:h-full</code> .</p>
<p>The default configuration can be overridden using a <code>tailwind.config.js</code> file and additional classes can be added to the framework so that there is never a need to use the  <code>style</code>  attribute.</p>
<p>Finally, TailwindCSS.com has a search utility that makes finding a class name extremely easy. If you need to know how to use a border radius, you can search it on the website and within seconds make the change to your code. This means that there is nothing to learn when picking up Tailwind CSS for the first time as its so easy to find on their website.</p>
<p><a target="_blank" href="https://tailwindcss.com/">Tailwind CSS - A Utility-First CSS Framework for Rapidly Building Custom Designs</a></p>
<h2 id="the-plan">The Plan</h2>
<p>In the next few posts, I will rewrite wallisconsultancy.co.uk using Next.js and TailwindCSS in order to achieve the following goals:</p>
<ul>
<li>Make it easier to maintain</li>
<li>Enhance the SEO using Next.js third-party libraries such as next-seo and next-image-optimizer </li>
<li>Host on Github pages to save money</li>
<li>Add a Recaptcha to reduce the amount of email spam received
And I’ll do all this while ensuring that the look of the page stays the same - with a few subtle tweaks.</li>
</ul>
<h2 id="round-up">Round up</h2>
<p>In this blog I introduced wallisconsultancy.co.uk and explained that over the next few blogs I am going to rebuild it using Next.js and Tailwind CSS.</p>
]]></content:encoded></item></channel></rss>