<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: piratematt</title>
    <description>The latest articles on DEV Community by piratematt (@piratematt).</description>
    <link>https://dev.to/piratematt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1045233%2Fd6979751-a0a9-429d-98e2-67a0956fd0f4.png</url>
      <title>DEV Community: piratematt</title>
      <link>https://dev.to/piratematt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/piratematt"/>
    <language>en</language>
    <item>
      <title>Last Month's Reading 24/01 – January 2024</title>
      <dc:creator>piratematt</dc:creator>
      <pubDate>Wed, 07 Feb 2024 01:46:44 +0000</pubDate>
      <link>https://dev.to/piratematt/last-months-reading-2401-january-2024-14ho</link>
      <guid>https://dev.to/piratematt/last-months-reading-2401-january-2024-14ho</guid>
      <description>&lt;p&gt;&lt;em&gt;Last Month's Reading; a non-exhaustive list.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Early Month
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.epicweb.dev/the-true-purpose-of-testing" rel="noopener noreferrer"&gt;The True Purpose of Testing&lt;/a&gt; by &lt;a href="https://kettanaito.com/" rel="noopener noreferrer"&gt;Artem Zakharchenko&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;"A test &lt;em&gt;becomes&lt;/em&gt; useful when it fulfills its purpose."&lt;/li&gt;
&lt;li&gt;"It is only when a test validates the intention that it becomes useful."&lt;/li&gt;
&lt;li&gt;Argues against tests validating &lt;em&gt;how&lt;/em&gt; and for tests validating &lt;em&gt;intention&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Potential counter thought: encapsulation often leads to tests validating how. These aren't inherently bad, asserting your building blocks behave as expected is good. This deserves more pondering.&lt;/li&gt;
&lt;li&gt;"That's what the automated tests are for." &amp;lt;— Oh, so we're talking about a specific type of test.&lt;/li&gt;
&lt;li&gt;I'd love to read an experienced TDD-er's take on this.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://dev.to/tbroyer/beyond-the-login-page-4hjd"&gt;Beyond the login page&lt;/a&gt; by &lt;a href="https://blog.ltgt.net/" rel="noopener noreferrer"&gt;Thomas Broyer&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;A list of questions to think about/tackle when developing authentication.&lt;/li&gt;
&lt;li&gt;I love how well this shows the complexity that often hides behind simplicity.&lt;/li&gt;
&lt;li&gt;"I don't think I ever implemented &lt;strong&gt;all&lt;/strong&gt; of the above &lt;em&gt;perfectly&lt;/em&gt;. There are always tradeoffs. But these are things to think about and make choices"&lt;/li&gt;
&lt;li&gt;He points out that this is &lt;em&gt;only&lt;/em&gt; authentication, not authorization. There's just &lt;em&gt;a lot&lt;/em&gt; to think about here.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://www.codu.co/articles/get-the-return-type-from-a-promise-in-typescript-m-eoui59" rel="noopener noreferrer"&gt;Get the Return Type from a Promise in TypeScript&lt;/a&gt; by &lt;a href="https://www.codu.co/niall" rel="noopener noreferrer"&gt;Niall Maher&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I have accessor methods responsible for direct DB access. Several of these are grabbing configured (via the DB) options that power selects for other records. When I passed them to my "select" components I was manually typing them. This felt wrong.&lt;/li&gt;
&lt;li&gt;I quick bit of web sleuthing later, thanks to Niall I had what I need 👇🏻
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;findRecordsForOptions&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RecordOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Awaited&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;findRecordsForOptions&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;Short, sweet, and to the point. Love it!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://remix.run/docs/en/main/guides/client-data?ck_subscriber_id=2210458388" rel="noopener noreferrer"&gt;New &lt;em&gt;Client Data&lt;/em&gt; docs section&lt;/a&gt; by &lt;a href="https://remix.run/" rel="noopener noreferrer"&gt;Remix&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;LoaderFunctionArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;clientLoader&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;serverLoader&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;ClientLoaderFunctionArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HydrateFallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This will always be the combined set of server + client data&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLoaderData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;"a bit of a sharp knife" – use wisely and cautiously&lt;/li&gt;
&lt;li&gt;Opt-in functionality&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mid – Late Month
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@nirbenyair/headless-components-in-react-and-why-i-stopped-using-ui-libraries-a8208197c268" rel="noopener noreferrer"&gt;Headless components in React and why I stopped using a UI library for our design system&lt;/a&gt; by &lt;a href="https://medium.com/@nirbenyair/about" rel="noopener noreferrer"&gt;Nir Ben-Yair&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Requirements: Accessibility, Theming, Uniqueness, Browser support, Functionality, Responsiveness, and Maintainability.&lt;/li&gt;
&lt;li&gt;UI Libraries are great, but also inevitably fail you when you start to get complex.&lt;/li&gt;
&lt;li&gt;Short version... Headless can help by being "well tested on multiple browsers, platforms, and devices and deal[ing] with edge cases that I never could or want to deal with myself: stuff like focus management, keyboard navigation, event listeners, accessibility attributes, valid markup and screen reader support"&lt;/li&gt;
&lt;li&gt;&lt;em&gt;I really like the way this seems to be abstracting away the hard details to the communal effort of Open Source but leaving "the work" to the developer's intention. Need to spend some more time digging in here.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://www.epicweb.dev/the-golden-rule-of-assertions" rel="noopener noreferrer"&gt;The Golden Rule of Assertions&lt;/a&gt; by &lt;a href="https://www.epicweb.dev/authors/artem-zakharchenko" rel="noopener noreferrer"&gt;Artem Zakharchenko&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Yes this is the second one from them this month. I suspect the future will bring more.&lt;/li&gt;
&lt;li&gt;Don't test &lt;em&gt;how&lt;/em&gt; (implementation), test &lt;em&gt;"the intention behind the system"&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Not... "has been called with". Instead try... "result is as expected".&lt;/li&gt;
&lt;li&gt;There is definitely a time and a place to test implementation. Maybe they don't live in your test suite forever, but if they help you develop working software faster... write them.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Skims and Honorable Mentions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bytes.dev/archives/252" rel="noopener noreferrer"&gt;Bytes #252 Your Weekly Dose of JS&lt;/a&gt; newsletter from &lt;code&gt;tyler@ui.dev&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/frosnerd/books-that-helped-me-become-a-tech-lead-3831"&gt;Books That Helped Me Become a Tech Lead&lt;/a&gt; by &lt;a href="https://dev.to/frosnerd"&gt;Frank Rosner&lt;/a&gt; – definitely going to have to come back to this one.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/fidalmathew/session-based-vs-token-based-authentication-which-is-better-227o"&gt;🔐 Session-Based vs. Token-Based Authentication: Which is better?🤔&lt;/a&gt; by &lt;a href="https://fidal-portfolio.vercel.app/" rel="noopener noreferrer"&gt;Fidal Mathew&lt;/a&gt; – Skimming and perusing the comments led me to &lt;em&gt;Beyond the login page&lt;/em&gt; mentioned above.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/ben/meme-monday-5h8i"&gt;dev.to Meme Monday&lt;/a&gt; – ah programming humor.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://bytes.dev/archives/258" rel="noopener noreferrer"&gt;Bytes #258 Better off Zed&lt;/a&gt; – Open source, faster than VS Code from the creators of Atom? I'm listening...&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>readinglist</category>
      <category>reading</category>
      <category>monthly</category>
    </item>
    <item>
      <title>Micro0001—Pattern: Custom Jest Error Messages</title>
      <dc:creator>piratematt</dc:creator>
      <pubDate>Thu, 07 Sep 2023 16:04:57 +0000</pubDate>
      <link>https://dev.to/piratematt/micro0001-pattern-custom-jest-error-messages-1lpb</link>
      <guid>https://dev.to/piratematt/micro0001-pattern-custom-jest-error-messages-1lpb</guid>
      <description>&lt;p&gt;&lt;em&gt;Series: &lt;strong&gt;Micro Blogs&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Entry: &lt;strong&gt;Micro0001—Pattern: Custom Jest Error Messages&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;First Published: &lt;strong&gt;September 2023&lt;/strong&gt;&lt;/em&gt; &lt;br&gt;
&lt;em&gt;Tags:&lt;/em&gt; &lt;strong&gt;microblog&lt;/strong&gt;, &lt;strong&gt;jest&lt;/strong&gt;, &lt;strong&gt;tdd&lt;/strong&gt;,  &lt;strong&gt;react&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Micro Blogs Series&lt;/strong&gt; – &lt;em&gt;Short posts of lessons learned, patterns, revelations, tips, missteps, etc. from the trenches of crafting software.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;span&gt;🔴 TL;DR&lt;/span&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Wrap your Jest expect statements with a try/catch block and add additional context to errors then re-throw them.&lt;/em&gt; This way the test failure itself is sufficient to understand what went wrong. Example from asserting a reducer deep-copies state reference values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducedState&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// we can ignore any non-reference values&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducedState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`state.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; —&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Props to &lt;a href="https://aartdenbraber.medium.com/custom-error-messages-with-jest-for-assertions-821c69e72389" rel="noopener noreferrer"&gt;&lt;em&gt;Aart den Braber&lt;/em&gt; for the inspiration!&lt;/a&gt; Note: this test only checks one-level deep; it is not a true deep-copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  🌆 Background: TDDing a Reducer
&lt;/h2&gt;

&lt;p&gt;Recently I was developing a module for use with &lt;a href="https://react.dev/reference/react/useReducer" rel="noopener noreferrer"&gt;&lt;code&gt;React.useReducer&lt;/code&gt;&lt;/a&gt;. I was using a few objects within my state: an array and a couple of Set’s. Long story short, as TDD was driving the design of my module, I encountered failing tests resulting from one the reference object values persisting longer than expected. If I recall correctly, one of my state manipulations ended up modifying a set that I was reusing across tests as expected data, rather than cleanly manipulating a set from a deep-copy of state as a reducer should. Oops! 🙈&lt;/p&gt;

&lt;p&gt;I successfully found a bug in my code. What’s next? Following TDD, the first step was writing a failing test covering the situation: &lt;em&gt;test that equality (&lt;code&gt;Object.is&lt;/code&gt; because that’s what React leverages) fails for any reference values within state (&lt;code&gt;typeof === 'object'&lt;/code&gt;) when running the reducer.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With my test written and failing, I took the simplest path to get started—hardcoding each key that was storing a reference value. Taking the next TDD step, I looked to refactor. I realized my current implementation would require me to manually update my test and solution anytime I added a new reference value to state, and I knew future-me was &lt;em&gt;highly unlikely to remember to do so.&lt;/em&gt; Refactoring, I modified my test to loop through each key-value pair and check for reference values. Now if/when I add new reference values to state my test will continue to serve its purpose! My test code ended up looking something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducedState&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// we can ignore any non-reference values&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducedState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked great, but it created a new pain. When a value fails to pass the assertion, no information about its associated key is included in the error message, requiring additional work upon failure to diagnose the culprit.&lt;/p&gt;

&lt;h2&gt;
  
  
  🦓 Pattern: Custom Jest Error Messages
&lt;/h2&gt;

&lt;p&gt;I’ve wanted custom jest error messages for a while. It’s not a frequent desire, but it comes up almost every time I write assertions inside loops. So, every now and then I search the interwebz to see what advice is out there. Most frequently, I find others advising me to add an additional package that provides this functionality. Sure this works, but I try to avoid increasing the attack surface and license management burden when I can. Custom Jest error messages never felt like enough functionality to justify the additional work. Particularly when adding a simple log statement—in the infrequent event of test failure—is sufficient to track down the specific scenario.&lt;/p&gt;

&lt;p&gt;Still… I’d rather have the test failure itself tell me exactly what was wrong, so once again I dove into the wilds of the internet. This time I found a pattern that was simple to parse and &lt;em&gt;didn’t require an entirely new package!&lt;/em&gt; Props to &lt;a href="https://aartdenbraber.medium.com/custom-error-messages-with-jest-for-assertions-821c69e72389" rel="noopener noreferrer"&gt;&lt;em&gt;Aart den Braber&lt;/em&gt; for the inspiration!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pattern: &lt;em&gt;wrap your Jest expect statements with a try/catch block and add additional context to errors then re-throw them.&lt;/em&gt; Now my test now looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducedState&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// we can ignore any non-reference values&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducedState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Pattern props to: https://aartdenbraber.medium.com/custom-error-messages-with-jest-for-assertions-821c69e72389&lt;/span&gt;
      &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`state.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; —&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way I know &lt;em&gt;exactly&lt;/em&gt; which referential value is causing the failure directly from the error message, while simultaneously retaining all the inbuilt goodness of the error jest already throws.&lt;/p&gt;

</description>
      <category>microblog</category>
      <category>jest</category>
      <category>tdd</category>
      <category>react</category>
    </item>
    <item>
      <title>piratematt.com v3—Upgrading to Next.js 13</title>
      <dc:creator>piratematt</dc:creator>
      <pubDate>Wed, 19 Jul 2023 15:30:53 +0000</pubDate>
      <link>https://dev.to/piratematt/piratemattcom-v3-upgrading-to-nextjs-13-450g</link>
      <guid>https://dev.to/piratematt/piratemattcom-v3-upgrading-to-nextjs-13-450g</guid>
      <description>&lt;p&gt;&lt;em&gt;Series: &lt;strong&gt;Personal/Professional Website&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Sub-series: &lt;strong&gt;piratematt.com v3&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Entry: &lt;strong&gt;02—Upgrading to Next.js 13&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;First Published: &lt;strong&gt;July 2023&lt;/strong&gt;&lt;/em&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Plans never survive contact with reality
&lt;/h2&gt;

&lt;p&gt;This wasn't supposed to be my second entry, but much like the &lt;a href="https://agilemanifesto.org" rel="noopener noreferrer"&gt;Agile Manifesto&lt;/a&gt; I value responding to change over following a plan. Next.js released their next major version, Next.js 13. Things are different… &lt;em&gt;How different&lt;/em&gt; is what this entry chronicles. Well… chronicles for a simple static site that is exported and served via github pages. If you're looking for a full comparison I suggest you start with &lt;a href="https://nextjs.org/blog/next-13" rel="noopener noreferrer"&gt;their own blog post on version 13&lt;/a&gt;. It's where I started and I found it helpful.&lt;/p&gt;
&lt;h2&gt;
  
  
  The origin of the pivot
&lt;/h2&gt;

&lt;p&gt;This upgrade journey began when I reached for Next.js to start a new project with a friend and was surprised by the &lt;a href="https://nextjs.org/docs/app/building-your-application/routing#the-app-router" rel="noopener noreferrer"&gt;app router&lt;/a&gt;. Caught flat footed I scrambled to explain a framework I no longer understood. Rather than simply reverting to an older version I was familiar with, we took some time to read through the docs and get the new lay of the land. Long story short, the project itself is on hold while my friend gets settled into a new job, so I switched my effort to understand Next.js 13 to my personal site. It certainly doesn't &lt;em&gt;need&lt;/em&gt; updated, but I want the practice, so here we are.&lt;/p&gt;
&lt;h2&gt;
  
  
  The work ahead of me
&lt;/h2&gt;

&lt;p&gt;Borrowing a technique from TDD (I'm still new-ish to TDD, but I've been making a concerted effort to practice it lately), I got started by creating a lightweight need/want TODO list. This enables me to give ideas the space they deserve as they crash into my brain, but in a way that lets me keep focus on the small step I'm trying to take when the crash occurs. My initial list was built from reading through the aforementioned blog post as well as the &lt;a href="https://nextjs.org/docs/pages/building-your-application/upgrading" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt;. These initial reads are done somewhere between a skimming pace and a studying pace. The goal isn't to recall everything, but to start to get a feel for the lay of the land. &lt;em&gt;What should I look out for? Where can I read more about how to get XYZ working? Should I switch to the shiny new ABC way of doing things, or stick with what's already got working?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I'm not going to include my actual TODO list because it's not a good communication tool. It's a good working tool. It's constantly modified; items get added, removed, re-added, and re-removed all the time. You get the picture. To outline the work ahead of me I'll split things into two lists.&lt;/p&gt;

&lt;p&gt;List 1: The differences in Next.js 13 I want to tackle in this effort.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switch to the new &lt;code&gt;next/font&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Migrate from the &lt;em&gt;pages router&lt;/em&gt; to the &lt;em&gt;app router&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Minimize my use of the &lt;code&gt;use client&lt;/code&gt; directive (prioritize server side components).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;List 2: A simplified overview of how this reflected in my TODO list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] upgrade to latest Next.js version&lt;/li&gt;
&lt;li&gt;[ ] upgrade ESLint version&lt;/li&gt;
&lt;li&gt;[ ] upgrade to Built-in Font&lt;/li&gt;
&lt;li&gt;[ ] switch from pages to app router

&lt;ul&gt;
&lt;li&gt;[ ] identify server vs client components and update appropriately&lt;/li&gt;
&lt;li&gt;[ ] rework how layouts work&lt;/li&gt;
&lt;li&gt;[ ] refactor &lt;code&gt;&amp;lt;Head&amp;gt;&lt;/code&gt; components to &lt;code&gt;metadata&lt;/code&gt; exports&lt;/li&gt;
&lt;li&gt;[ ] refactor from styled-components to CSS modules (styled-components require runtime javascript and therefore must be client components)&lt;/li&gt;
&lt;li&gt;[ ] refactor from pages/404.js to not-found.js file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Task 0 – Create a home for the effort and update to the latest versions
&lt;/h2&gt;

&lt;p&gt;I wanted a safe isolated place in my github repository where I could tackle this update effort, so I kicked things off by creating a new branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; next13
git push origin next13
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I have a safe space I can frequently push my WIP (work in progress) states to just in case my local machine melts.&lt;/p&gt;

&lt;p&gt;So… it turns out I'm already using Next.js 13, &lt;code&gt;"next": "^13.1.6",&lt;/code&gt; to be exact. &lt;em&gt;What a twist! &lt;sup&gt;&lt;a href="https://www.adultswim.com/videos/robot-chicken/what-a-twist" rel="noopener noreferrer"&gt;ref&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt;. How did I uncover this? As I was doing my first readthrough I saw they called out that &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; components no longer needed a child &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag to function, and I thought to myself I've &lt;em&gt;never&lt;/em&gt; had to do that to get &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; to work. So I took a quick peek at my package.json git history… and, yup, I've always been on Next.js 13. Fortunately for my sanity it's clear looking at the &lt;a href="https://nextjs.org/docs" rel="noopener noreferrer"&gt;nextjs.org docs&lt;/a&gt;, that there has indeed been a recent switch to the &lt;em&gt;app router&lt;/em&gt;, so my confusion wasn't merely the result of some fever dream. 🤞🏻being on Next 13 already makes my “upgrade” easier.&lt;/p&gt;

&lt;p&gt;To get the latest and greatest for both Next.js and ESLint, I run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i next@latest react@latest react-dom@latest eslint-config-next@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This puts me at the following version (extracted from package.json) 👇🏻&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^13.4.7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"eslint-config-next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^13.4.7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then execute &lt;code&gt;npm run lint&lt;/code&gt; in my terminal and fix any reported issues (that aren't a future TODO list item).&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 1 – Switch to the new &lt;code&gt;next/font&lt;/code&gt; and away from the &lt;code&gt;@next/font&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;After a brief read through the &lt;a href="https://nextjs.org/docs/pages/building-your-application/optimizing/fonts" rel="noopener noreferrer"&gt;Font Optimization section of the upgrade guide&lt;/a&gt;, I try the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// import { Share_Tech_Mono } from '@next/font/google';&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Share_Tech_Mono&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/font/google&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a bit of manual checking, I realize this is all I need to do, so I also go ahead and uninstall the old &lt;code&gt;@next/font&lt;/code&gt; and delete the commented out import line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm uninstall @next/font
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was quick and painless! Another item checked off my TODO list! 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Task 2 – Bye-bye “Pages” router, hello “App” router
&lt;/h2&gt;

&lt;p&gt;I wish I could tell you this was an easy process with clear steps that you can follow in the same order I accomplished them in and succeed, but it ended up being more of an ever expanding FILO (first in, last out) stack of TODOs. I'll do my best to use headers to provide some clarity, but things may jump around a bit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 2.1 – Refactor the home page to use the &lt;em&gt;app router&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Trying to take the smallest step I could, I set my targets on replacing the existing home page, &lt;code&gt;pages/index.tsx&lt;/code&gt;, with a “hello world” home page powered by the &lt;em&gt;app router&lt;/em&gt;. To get started, I added an &lt;code&gt;/app&lt;/code&gt; directory and placed a hello world &lt;code&gt;page.tsx&lt;/code&gt; file within it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Home – piratematt.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HomePage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello Home Page&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I changed the name of my &lt;code&gt;pages/index.tsx&lt;/code&gt; to &lt;code&gt;pages/old_index.tsx&lt;/code&gt; to prevent conflicts, and restarted my dev server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick navigation to &lt;code&gt;http://localhost:3000/&lt;/code&gt; showed me my shiny new “hello world” home page. Great! Things are moving along. Next I simply copy the contents of my old home page, styled-components and all. Optimistically I refresh my page. Uh oh, things are no longer working. None of the updates I made to &lt;code&gt;_app.tsx&lt;/code&gt; and &lt;code&gt;_document.tsx&lt;/code&gt; to get styled-components working are taking effect. Hmmm… time to add a new task to the stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 2.2 – Refactor &lt;code&gt;_app.tsx&lt;/code&gt; and &lt;code&gt;_document.tsx&lt;/code&gt; to the new &lt;code&gt;app/layout.tsx&lt;/code&gt; structure
&lt;/h3&gt;

&lt;p&gt;Skipping a lot of narratively unnecessary trial and error let's just say this is where my desire to preserve my use of styled-components left me. I discovered each page/component leveraging styled-components needed the &lt;code&gt;use client;&lt;/code&gt; directive, which felt wrong because most of my page content is static “brochure content” and has no business not being rendered server-side. To that end, I elected to add another task to my quickly growing stack: &lt;em&gt;refactor to CSS modules and prioritize server-side rendering&lt;/em&gt;. (Yes I realize that exporting this specific Next.js project makes it of little consequence in the end. Nevertheless, I saw value in practicing limiting my client-side components.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 2.3 – Refactor styled-components (CSS-in-JS) to CSS modules
&lt;/h3&gt;

&lt;p&gt;This is a task that gets repeatedly put back on the top of my task stack as I tackle pages one at a time. Rather than bore you with that repetitive process, I'll leave a sample of some salient details and move on. Overall the process was fairly simple. Rather than having styles declared inline within my &lt;code&gt;.tsx&lt;/code&gt; files, declarations are refactored/extracted to a &lt;code&gt;global.css&lt;/code&gt; file and a specific &lt;code&gt;.module.css&lt;/code&gt; file which are subsequently imported. If you want to read more, I suggest the &lt;a href="https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration#step-7-styling" rel="noopener noreferrer"&gt;Styling section of the upgrade docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Global styles and themes were established in a config file in the root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// themeConfig.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;bgDark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#000000e6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GlobalStyles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
  /* redacted for brevity */
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These global styles and themes are applied in the &lt;code&gt;pages/_app.tsx&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/_app.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ThemeProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;styled-components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GlobalStyles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../themeConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* redacted for brevity */&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;GlobalStyles&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* redacted for brevity */&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within  each component “CSS-in-JS” is written leveraging string interpolation to access the theme variables where desired.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Footer.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;styled-components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FooterWrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;styled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="s2"&gt;`
  background: &lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bgDark&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Usage of `&amp;lt;FooterWrapper /&amp;gt;` in component render redacted for brevity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Global CSS and theme variables are set in an &lt;code&gt;app/global.css&lt;/code&gt; file. Theme variables are set using the &lt;code&gt;:root&lt;/code&gt; pseudo-class selector to make them available everywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* app/global.css */&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--bg--dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#000000&lt;/span&gt;&lt;span class="n"&gt;e6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* redacted for brevity */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These globals are then included in the base layout of the app router. There is no need to specifically apply them via &lt;code&gt;className&lt;/code&gt; or any other method. This is taken care of for us by Next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/layout.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./global.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// default export redacted for brevity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;.module.css&lt;/code&gt; file is created for specific components/pages. It is best practice to use &lt;em&gt;lowerCamelCase&lt;/em&gt; for class names as it is easier to dot-access them after they are imported into your component/page files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* components/Footer.module.css */&lt;/span&gt;
&lt;span class="nc"&gt;.footerWrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--bg--dark&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This module specific CSS is imported into a &lt;code&gt;styles&lt;/code&gt; variable and then applied where desired by specifying a &lt;code&gt;className&lt;/code&gt; for whichever HTML tag or React component you want the styling to apply. Next handles unique class name generation, bundling, etc. all for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Footer.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Footer.module.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;footerWrapper&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* redacted for simplicity */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt; – Overall, it's fine. Some things are harder/worse, sure, but I found no hills I was willing to die on.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I used significantly less &lt;code&gt;use client;&lt;/code&gt; directives to get my CSS-in-JS working.&lt;/li&gt;
&lt;li&gt;There's no extra system to learn, it's normal CSS.&lt;/li&gt;
&lt;li&gt;Next handles all the hard stuff without customization: unique class name management, bundling, etc.&lt;/li&gt;
&lt;li&gt;CSS modules keep the CSS that's affecting your components alongside the components themselves and not hidden away in some massive CSS file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons&lt;/strong&gt; – Overall, CSS variables are less powerful than systems like SASS/Less/CSS-in-JS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can't use CSS variables in media queries yet, so common breakpoints must be manually kept in sync.&lt;/li&gt;
&lt;li&gt;You can't nest selectors in CSS, necessitating more lines of CSS to do the same thing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After I had fully refactored &lt;em&gt;all&lt;/em&gt; components and pages to CSS modules I made sure to uninstall the no longer necessary styled-component packages and removed the &lt;code&gt;pages&lt;/code&gt; directory entirely. (Note: I made sure I had a commit to roll back to just in case I missed something. You might want to do the same if you're facing a similar migration effort.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm uninstall styled-components babel-plugin-styled-components @types/styled-components
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At some point in my conversion process I found that my old way of applying my font needed modification to work with CSS modules, so I paused where I was and put another item on the top of the task stack.&lt;/p&gt;

&lt;h4&gt;
  
  
  Task 2.3.2 – Update font usage for CSS modules
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Previously&lt;/strong&gt; I was using a class name and importing the font into multiple files. Example 👇🏻:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Footer.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Share_Tech_Mono&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@next/font/google&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Footer.module.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Footer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;footerWrapper&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Share_Tech_Mono&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* redacted for simplicity */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now&lt;/strong&gt; I set a CSS variable, &lt;em&gt;once,&lt;/em&gt; in the base layout of the app router and simply use it in whatever component/page CSS module file I want&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/layout.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shareTechMono&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Share_Tech_Mono&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;400&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;latin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;variable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--font--share-tech-mono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;monospace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;BaseLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* redacted for brevity */&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;shareTechMono&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;variable&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* redacted for brevity */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* components/Footer.module.css */&lt;/span&gt;
&lt;span class="nc"&gt;.footerWrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--font--share-tech-mono&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;monospace&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;monospace&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order of task stacking in this narrative and their actual order have well and truly diverged. For the sake of clarity we'll say my next discovery that made its way to the top of my stack arose during the conversion of my second page from the &lt;em&gt;pages router&lt;/em&gt; and to the &lt;em&gt;app router&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 2.4 – Refactor my second page to use the &lt;em&gt;app router&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;I often find the first task I take on when refactoring to a new framework/pattern to be a relatively straightforward process. The issues only really start to rear their heads when I tackle a second task. Converting my second page was no exception. The first thing I noticed was that I had too many &lt;code&gt;page.tsx&lt;/code&gt; files open in my editor and that &lt;em&gt;is a hill I'm willing to die on.&lt;/em&gt; It's hands down my least favorite commonly accepted pattern in all of development. Even one &lt;code&gt;index.js&lt;/code&gt; file irks me, because rarely is that file actually an index. Grrr. Anyways, I'll step away from the soapbox and digress.&lt;/p&gt;

&lt;h4&gt;
  
  
  Task 2.4.1 – Avoid &lt;code&gt;page.tsx&lt;/code&gt; hell
&lt;/h4&gt;

&lt;p&gt;I wish I could remember who introduced me to this pattern that alleviates my frustrations, but alas I do not remember. The strategy itself is fairly simple. Take on a little bit of extra boilerplate now, so the rest of your time working in the codebase is significantly more enjoyable. Every page of mine adopts the following pattern. &lt;code&gt;page.tsx&lt;/code&gt; is pure boilerplate, and actual code is written in files that follow this pattern: &lt;code&gt;UsefulName.page.tsx&lt;/code&gt;. For instance, a page accessible at &lt;code&gt;http://localhost:3000/second-page&lt;/code&gt; would look something like👇🏻.&lt;/p&gt;

&lt;p&gt;This boilerplate page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/second-page/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SecondPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pageMetadata&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Second.page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pageMetadata&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;SecondPage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which serves the contents of this file where my actual code lives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/second-page/Second.page.tsx`&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Second – piratematt.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SecondPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* redacted for brevity */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: I fully expect this boilerplate pattern to be rendered unnecessary by a future Next.js update and/or a configuration. I know I'm not the only developer willing to die on this hill. I spent some time digging around to see if it already existed, but I exhausted the amount I was willing to spend trying to alleviate my frustrations using “official” means and methods. Sometimes you have to know when to go with the sub-optimal solution you can do quickly and confidently over the optimal solution you don't yet know.&lt;/p&gt;

&lt;p&gt;We’re once again departing from the actual order of events, but for the sake of the narrative let’s say this is when I came upon my next task for the top of the stack. My second page required a different layout than any of my other pages. Okay, it didn’t &lt;em&gt;need&lt;/em&gt; a different layout, but I’m using one because I want to.&lt;/p&gt;

&lt;h4&gt;
  
  
  Task 2.4.2 – Handle multiple layouts; refactor away from the &lt;a href="https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts#per-page-layouts" rel="noopener noreferrer"&gt;getLayout pattern&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Previously I was using the &lt;a href="https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts#per-page-layouts" rel="noopener noreferrer"&gt;&lt;code&gt;page.getLayout&lt;/code&gt; pattern&lt;/a&gt; to dynamically pull in a different layout for a single, on-off page. I &lt;em&gt;could have&lt;/em&gt; duplicated layouts, but adhering to DRY (don't repeat yourself) seemed the wiser path. Within the app router, I failed to find a way to repeat my &lt;em&gt;getLayout&lt;/em&gt; pattern. Instead I wound up refactoring my code structure to make use of &lt;em&gt;route groups&lt;/em&gt; and several nested &lt;code&gt;layout.tsx&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;Oddly enough—considering my previous rant about &lt;code&gt;page.tsx&lt;/code&gt; hell—I adopted a slightly different pattern to prevent &lt;code&gt;layout.tsx&lt;/code&gt; hell. This is likely due to the fact that I only ever plan on having two layouts, but I knew I'd want to support &lt;code&gt;n&lt;/code&gt; pages. When things were all said and done, I ended up with 3 &lt;code&gt;layout.tsx&lt;/code&gt; files, 2 route groups, and 1 &lt;code&gt;CommonLayout.tsx&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;Before I dive into example code, let's talk briefly about route groups. Simply put, &lt;em&gt;route groups&lt;/em&gt; are a &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/route-groups" rel="noopener noreferrer"&gt;special naming convention for a folder&lt;/a&gt;, &lt;code&gt;(normal-layout)&lt;/code&gt; for example, that enable us to logically group pages together without adding to the route's URL path. So &lt;code&gt;app/(normal-layout)/about/page.tsx&lt;/code&gt; is accessible at &lt;code&gt;http://localhost:3000/about&lt;/code&gt; and &lt;code&gt;app/(abnormal-layout)/second-page/page.tsx&lt;/code&gt; is accessible at &lt;code&gt;http://localhost:3000/second-page&lt;/code&gt;. With that out of the way, consider the following example code.&lt;/p&gt;

&lt;p&gt;First we have the &lt;code&gt;CommonLayout&lt;/code&gt; component that is imported and utilized in every &lt;code&gt;layout.tsx&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/CommonLayout.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NavHeader&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../components/NavHeader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Footer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../components/Footer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CommonLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;abnormal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;abnormal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NavHeader&lt;/span&gt; &lt;span class="na"&gt;abnormal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;abnormal&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Footer&lt;/span&gt; &lt;span class="na"&gt;abnormal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;abnormal&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next up we have the base layout that applies to &lt;em&gt;every&lt;/em&gt; page the app router serves. You can see it's only doing the bare minimum, leaving the specifics of the layout up to each route group.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/layout.tsx&lt;/span&gt;
&lt;span class="c1"&gt;// styles, fonts, etc. redacted for brevity&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;BaseLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we have our two route group &lt;code&gt;layout.tsx&lt;/code&gt; files, which are nearly identical, except for a single boolean switch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/(normal-layout)/layout.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CommonLayout&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../CommonLayout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;NormalLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CommonLayout&lt;/span&gt; &lt;span class="na"&gt;abnormal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CommonLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/(abnormal-layout)/layout.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CommonLayout&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../CommonLayout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AbnormalLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CommonLayout&lt;/span&gt; &lt;span class="na"&gt;abnormal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CommonLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With our two route groups now setup, our pages reside and are accessible at (note: I removed my Second.page.tsx pattern to simplify things):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First/Home Page lives at &lt;code&gt;app/(normal-layout)/page.tsx&lt;/code&gt; and is accessible at &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Second Page lives at &lt;code&gt;app/(abnormal-layout)/second-page/page.tsx&lt;/code&gt; and is accessible at &lt;code&gt;http://localhost:3000/second-page&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Originally I didn't even have &lt;code&gt;app/layout.tsx&lt;/code&gt;. I only had two layout files, one in each route group, but the next task we’re throwing on top of the stack made it a necessary refactor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Task 2.5 – Refactoring &lt;code&gt;pages/404.tsx&lt;/code&gt; to &lt;code&gt;app/not-found.tsx&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The content of my 404/not-found page was a straightforward port. I simply moved the content from &lt;code&gt;pages/404.tsx&lt;/code&gt; to &lt;code&gt;app/not-found.tsx&lt;/code&gt;. Figuring out what level the layout needed abstracted to and how to minimize repeating myself took some trial and error. Long story short my base &lt;code&gt;app/layout.tsx&lt;/code&gt; doesn't do any of the actual layout lifting. As you can see above, that's all handled by my &lt;code&gt;CommonLayout.tsx&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Previously&lt;/strong&gt; I had something like this, leveraging the aforementioned &lt;code&gt;getLayout&lt;/code&gt; pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/404.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PageType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../layouts/MainLayout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PageType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page404&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* redacted for brevity  */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getLayout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getLayout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now&lt;/strong&gt; I have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/not-found.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CommonLayout&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./CommonLayout&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page404&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CommonLayout&lt;/span&gt; &lt;span class="na"&gt;abnormal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* redacted for brevity */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;CommonLayout&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Task 2.6 – Refactoring &lt;code&gt;&amp;lt;Head&amp;gt;&lt;/code&gt; components to &lt;code&gt;metadata&lt;/code&gt; exports
&lt;/h3&gt;

&lt;p&gt;If you have extra sharp eyes, you may have picked up on the way the &lt;em&gt;app router&lt;/em&gt; handles page metadata when I was talking through my &lt;code&gt;page.tsx&lt;/code&gt; boilerplate. No longer will Next extract the &lt;code&gt;&amp;lt;Head&amp;gt;&lt;/code&gt; component from a page file. Instead you simply export a named &lt;code&gt;metadata&lt;/code&gt; object which Next ensures gets converted into header tags and included in the server response. If you attempt to export a metadata object from a client-rendered component/page with the &lt;code&gt;use client;&lt;/code&gt; directive, you'll find Next yells at you. You can only modify page metadata server-side.&lt;/p&gt;

&lt;p&gt;Example, what &lt;strong&gt;previously&lt;/strong&gt; looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/about.tsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AboutPage&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;About - piratematt.com&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;A wall of text covering this &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"pirate"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; thing. Complete with a &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tl;dr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PageWithTitle&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"about"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* redacted for brevity */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;PageWithTitle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Now&lt;/strong&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/about/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;About – piratematt.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A wall of text covering this "pirate" thing. Complete with a tl;dr.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AboutPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="cm"&gt;/* redacted for brevity */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Take-aways &amp;amp; what's next for this series
&lt;/h2&gt;

&lt;p&gt;I like what they're trying to do with Next.js 13 and the &lt;em&gt;app router&lt;/em&gt;—be opinionated about structure to increase clarity. The necessity of the &lt;code&gt;use client;&lt;/code&gt; directive to leverage state, etc. makes it crystal clear what can be rendered server-side and what requires a client. When all was said and done, I had &lt;em&gt;only a single&lt;/em&gt; component that needed the client directive. If it were up to me I'd require a &lt;code&gt;UsefulName.page.tsx&lt;/code&gt; pattern instead of just &lt;code&gt;page.tsx&lt;/code&gt; but I have a workaround I like well enough to continue using Next. &lt;/p&gt;

&lt;p&gt;At the end of the day, if you leverage &lt;a href="https://nextjs.org/docs/pages/building-your-application/upgrading" rel="noopener noreferrer"&gt;Next's upgrade guides&lt;/a&gt; you won't run into any show-stopping issues when updating to Next.js 13—at least not for a static brochure site. I suspect you may even come to prefer the changes by the end of your efforts, as I have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's next in this series?&lt;/strong&gt; As a part of my upgrade efforts I've taken another look at my planned entries for the remainder of the series and cut it down quite a bit. Next's documentation already does a great job covering some of the topics I had planned, and for some of the others, I simply don't use the associated tools/technologies in this project anymore. Therefore, I've decided to preserve my writing time for other efforts. As it stands now here's the latest and greatest plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] &lt;a href="https://dev.to/piratematt/piratemattcom-v3-overview-getting-started-2ghn"&gt;v3 – Overview &amp;amp; Getting Started&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[x] Upgrading to Next.js 13 (this entry!)&lt;/li&gt;
&lt;li&gt;[ ] Deploying Next.js to Github Pages&lt;/li&gt;
&lt;li&gt;(&lt;em&gt;considering&lt;/em&gt;) Final thoughts, conclusions, and future ideas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've made it this far, thanks for reading! If you have comments, critiques, cautions, or clairvoyances please feel welcome to share them!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>personalwebsite</category>
      <category>nextjs13</category>
      <category>upgrade</category>
    </item>
    <item>
      <title>piratematt.com v3—Overview &amp; Getting Started</title>
      <dc:creator>piratematt</dc:creator>
      <pubDate>Mon, 24 Apr 2023 18:51:01 +0000</pubDate>
      <link>https://dev.to/piratematt/piratemattcom-v3-overview-getting-started-2ghn</link>
      <guid>https://dev.to/piratematt/piratemattcom-v3-overview-getting-started-2ghn</guid>
      <description>&lt;p&gt;&lt;em&gt;Series: &lt;strong&gt;Personal/Professional Website&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Sub-series: &lt;strong&gt;piratematt.com v3&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Entry: &lt;strong&gt;01—Overview and Getting Started&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;First Published: &lt;strong&gt;April 2023&lt;/strong&gt;&lt;/em&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the (sub)series
&lt;/h2&gt;

&lt;p&gt;Hello fellow internet denizens, welcome to this series! I’ll be covering my journey crafting my personal/professional website. Expect some narratives, some musings on tools/techniques chosen, some steps to how I got things working, and some reflections on lessons learned. Although slightly confusing, my first subject is actually the third version of my site. I might go back and add entries/sub-series about my first two versions but, to be frank, I don’t think they’re worth writing about.&lt;/p&gt;

&lt;p&gt;Without further ado, let’s dive into my most recent version of &lt;a href="https://piratematt.com/" rel="noopener noreferrer"&gt;piratematt.com&lt;/a&gt; with a question:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How did I get to the third version?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Version 1 was a fairly standard linux box—CentOS if my memory serves me well—spun up on a free year of AWS. On this box, I ran Node.js with Express and either Jade or Pug templates. To be honest, I have no desire to search my branch history for a definitive answer. It was a fine setup, but it was overkill. The site didn’t &lt;em&gt;do anything&lt;/em&gt;. In the end, once my free year was up, I decided to cut the cost and build a new version.&lt;/p&gt;

&lt;p&gt;I set aside very little time to build Version 2, so I kept it a quick and dirty endeavor. My desire for something free led me to github pages. I leveraged vanilla HTML, CSS, and JavaScript files to get a static site hammered out in a few days (including a few design tweaks). Like I said, I wanted something fast, not something good, and this fit the bill. It was a slightly nostalgic experience, reminding me of when I was first learning to code—before learning about loops, I would just copy and paste things around that I wanted repeated. This version worked but it was a pain to update and I felt guilty because it clearly violated the principles of tight cohesion, and loose coupling.&lt;/p&gt;

&lt;p&gt;These feelings of guilt and the desire to tackle a smaller project with next.js (more on this decision later) led me to Version 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Touching on the frameworks/tools/technologies I selected for v3
&lt;/h2&gt;

&lt;p&gt;One of the first things I tackled in version 3 was picking what frameworks/tools/technologies I wanted to use. I knew upfront that I wanted a next.js project that I would build locally and host statically via github pages, but the rest took a little bit of thinking. I’ll briefly touch on my final selections below. In no particular order, here’s a list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Pages&lt;/strong&gt; – static hosting from the &lt;code&gt;/docs&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React&lt;/strong&gt; – shiny (and popular) front end library&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next.js&lt;/strong&gt; – “the React Framework for the Web”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styled components&lt;/strong&gt; – ”visual primities for the component age”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jest + React Testing Library&lt;/strong&gt; – &lt;em&gt;Jest&lt;/em&gt; as the testing framework, and the &lt;em&gt;React flavor of Testing Library&lt;/em&gt; to align tests closely with actual user behavior as well as keeping accessibility in the forefront&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google fonts&lt;/strong&gt; – Next.js has &lt;a href="https://nextjs.org/docs/basic-features/font-optimization" rel="noopener noreferrer"&gt;some nifty built-in handling for google fonts&lt;/a&gt;, even when building and exporting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; – “JavaScript with syntax for types”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;namecheap&lt;/strong&gt; – purchasing and configuring domains&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;GitHub Pages&lt;/a&gt; – static hosting from the &lt;code&gt;/docs&lt;/code&gt; directory
&lt;/h3&gt;

&lt;p&gt;I was already using GitHub Pages for Version 2 of my site. Since I’m not adding any additional functionality to my site for Version 3, there was no reason to select anything else. It meets all my needs. It’s simple and easy static hosting. With a quick addition of a TXT record to my domain service, I set up my own url—&lt;a href="https://piratematt.com" rel="noopener noreferrer"&gt;piratematt.com&lt;/a&gt;. With the check of a box, I enforce https. Lastly, it's got low-to-no operating costs—I even consider &lt;em&gt;free to me&lt;/em&gt; because I’ve been paying for github for years now for other reasons. (Note: it can still be &lt;em&gt;free to you&lt;/em&gt; if you don’t want to pay, just make sure your repository is public.)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://react.dev/" rel="noopener noreferrer"&gt;React&lt;/a&gt; – shiny (and popular) front end library
&lt;/h3&gt;

&lt;p&gt;I selected React for simple reasons. I’m familiar with it having used it professionally, and have been looking for an excuse to flex those muscles again. I also enjoy it. I find it particularly powerful when building reusable, extensible visual component libraries. Data can get a bit tricky, but that’s been my experience no matter what web framework I’ve used.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; – “the React Framework for the Web”
&lt;/h3&gt;

&lt;p&gt;To make a long story short, I was using a KTDA, Kepner Tregoe Decision Analysis, to pick a web application framework for a different project (Orbital Goals—software to help me keep track of my own particular style of annual goal planning). Next.js came out as one of the top options. I wanted to understand next.js better before I made my final call, but I was feeling overwhelmed by unfamiliar terms, patterns, etc.  I knew I’d need to do more than read documentation and complete a hello-world-project to attain the knowledge I was seeking.&lt;/p&gt;

&lt;p&gt;Rather than diving into an unstarted project with an unfamiliar framework, I combined two items on my todo list, merging my search for knowledge with my desire to modernize piratematt.com. I could easily see that converting my existing static brochure site to a new framework was a significantly smaller step than building authentication, APIs, databases, a UI library, etc. from scratch in said framework.&lt;/p&gt;

&lt;p&gt;Looking back, I’m glad I did. Tackling a smaller problem, I found it much easier to get comfortable navigating next.js documentation, build framework specific vocabulary, and start employing effective search terms. I was especially pleasantly surprised how quickly I was able to craft a stable build, export, qa, and deploy process. Even the additional frameworks/tools/technologies I choose—such as styled components and TypeScript—didn’t slow me down.&lt;/p&gt;

&lt;p&gt;I’m pleased to state that Next.js is quite well built for my purposes → &lt;em&gt;Craft a personal/professional website with modern tools, techniques, and frameworks, establishing an easy way to build it statically and host it simply on GitHub Pages.&lt;/em&gt; I’m glad I selected it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://styled-components.com/" rel="noopener noreferrer"&gt;Styled Components&lt;/a&gt; – “visual primitives for the component age”
&lt;/h3&gt;

&lt;p&gt;Styled components were introduced to me by a coworker who was jazzed about them (shoutout to Peeler). At first I found them frustrating. Too much time spent in the CSS trenches ingrained in me a preference for only applying styling through sleek, powerful, cascading style sheets that carefully and intentionally leverage specificity to apply cohesive styling throughout a site with a single source of truth and little to no repetition. &lt;/p&gt;

&lt;p&gt;After a while, I came to enjoy the simplicity of encapsulating the styles affecting a component right there within the component itself. Support for themes within Styled Components and the reusable nature of React components produced a similar result to well crafted CSS strategies, but without the extra mental burden of knowing what CSS in what files were affecting the elements you were working on. It still doesn’t feel as elegant to me, but experience has taught me to trade elegance for ease of understanding 9 times out of 10. I find the benefit teams gain when onboarding new members is typically all the payoff needed, but more and more I find myself thanking past-me for choosing the clear path over a clever one when I’m revisiting code I don’t remember crafting.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; + &lt;a href="https://testing-library.com/docs/react-testing-library/intro/" rel="noopener noreferrer"&gt;React Testing Library&lt;/a&gt; – &lt;em&gt;Jest&lt;/em&gt; as the testing framework, and the &lt;em&gt;React flavor of Testing Library&lt;/em&gt; to align tests closely with actual user behavior as well as keeping accessibility in the forefront
&lt;/h3&gt;

&lt;p&gt;Both of these were selected, in addition to my appreciation for them, because of my prior professional experience with them. I find Jest to be one of the simplest testing frameworks I’ve ever used and I appreciate the philosophy behind Testing Library. Its default attention to accessibility ensures I adopt an empathetic mindset when crafting code. Its focus on mimicking actual user interactions gives me confidence I’m testing impactful behavior. That said, it’s a hammer and not everything is a nail. I often find myself writing typical unit tests for utilities, data-transformers, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://fonts.google.com/" rel="noopener noreferrer"&gt;Google fonts&lt;/a&gt; – Next.js has &lt;a href="https://nextjs.org/docs/basic-features/font-optimization" rel="noopener noreferrer"&gt;some nifty built-in handling for google fonts&lt;/a&gt;, even when building and exporting
&lt;/h3&gt;

&lt;p&gt;Google fonts is my goto for finding fonts I’m actually licensed to use. I was delighted to find Next.js had a way to natively, and performantly, pull them in.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt; – “JavaScript with syntax for types”
&lt;/h3&gt;

&lt;p&gt;I’m slowly coming aboard the TypeScript train 🚂. Professionally I have the most experience with &lt;a href="https://flow.org/" rel="noopener noreferrer"&gt;Flow(type)&lt;/a&gt;, but it seems clear to me that TypeScript is winning the battle of prevalence. I selected TypeScript for this project to get some more practice with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.namecheap.com/" rel="noopener noreferrer"&gt;namecheap&lt;/a&gt; – purchasing and configuring domains
&lt;/h3&gt;

&lt;p&gt;Namecheap was the first place I purchased a domain, and I’ve stuck with them ever since. They also do some other things like hosting, email, etc. but I’ve never felt the need for their other services. Domain purchasing and identity protection on those domains is good enough for me!&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussing my approach to getting started with v3
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1 – Read the docs
&lt;/h3&gt;

&lt;p&gt;My primary goal with any new tool/framework/etc. is to be able to orient myself. What is it good at doing? What is it bad at doing? What vocabulary does it establish? How quickly will I be able to find answers within the documentation? When do I need to broaden my search to the internet? What is the magic combination of search terms I need to use to get actually usable results?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What’s the best way I have to become oriented?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Well as an old professor of mine once advised me: &lt;em&gt;sometimes you just have to sit down and read the &lt;a href="https://en.wikipedia.org/wiki/Man_page" rel="noopener noreferrer"&gt;man page&lt;/a&gt;.&lt;/em&gt; Of course modern frameworks don’t have man pages in the traditional sense (although many lower-level tools do), the advice still applies. Yes, you will be overwhelmed. No, everything won’t stick. The goal isn’t to come out of your first readthrough as an expert. Instead, focus your first readthrough on absorbing as much as you can.&lt;/p&gt;

&lt;p&gt;With this in mind, I sat down and read through the Next.js docs. I was pleasantly surprised to find that they begin with well crafted Getting Started and Basic Features sections. Being new to next.js, but not to Web Frameworks, I skimmed the advanced topics unless I knew it applied to something I wanted to do or use for this project (static HTML export, custom 404 page, etc.). Before I knew it, things were running locally and I was beginning to work through transforming my vanilla HTML/JS/CSS files from Version 2 into reusable React components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 – Incrementally deliver locally until a stable “MVP” state is reached
&lt;/h3&gt;

&lt;p&gt;Once I completed my first readthrough of the docs and got “hello world” out of the way, I sat down and identified some iterative deliverables and identified what I considered to be the MVP (minimum viable product) and SLC (simple, loveable, complete) states for the project. These terms don’t map super well to projects with solely internally focused drivers like this website rebuild, but even in nonideal circumstances I find them useful focal points.&lt;/p&gt;

&lt;p&gt;Oversimplifying the giant checklist in my README file, the deliverables I identified look something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have Next.js serve the existing HTML/JS/CSS so I can start incrementally updating each page.&lt;/li&gt;
&lt;li&gt;Get Styled Components up and running locally, playing nice with Next.js’s dev server.&lt;/li&gt;
&lt;li&gt;Incrementally replace each version 2 page with React Components.

&lt;ul&gt;
&lt;li&gt;Start with the header and footer because these are shared by nearly every page.&lt;/li&gt;
&lt;li&gt;Abstract reused components as you go.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;&lt;em&gt;MVP state is reached when each existing page is converted to React components leveraging Styled Components.&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;

&lt;li&gt;Develop deployment process.&lt;/li&gt;

&lt;li&gt;Add Jest, and React Testing Library.

&lt;ul&gt;
&lt;li&gt;Write tests for the few non-presentational components.&lt;/li&gt;
&lt;li&gt;Update deployment process to include tests.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Convert the project to TypeScript, utilizing the deployment process along the way.&lt;/li&gt;

&lt;li&gt;Improve the deployment process.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;&lt;em&gt;SLC state is reached when tests are written/passing, and all files are converted to TypeScript files.&lt;/em&gt;&lt;/strong&gt;&lt;/li&gt;

&lt;li&gt;Write and publish blog entries about the experience.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Of course my carefully laid plans didn’t survive contact with reality, but they stayed relatively close. I’ll share a few highlights of my real-time adaptations below.&lt;/p&gt;

&lt;p&gt;I never did get my existing V2 files working alongside Next.js powered pages. After a brief attempt I realized the way Next.js handles images, links, head tags, and the like meant I couldn’t simply return full, unaltered HTML files from within the framework. &lt;em&gt;I could add them after next.js produced its static output&lt;/em&gt;, but I didn’t know how that was going to work yet and didn’t want to tackle it until I had something to actually deploy, so I took this deliverable off the list.&lt;/p&gt;

&lt;p&gt;I successfully componentized almost all of my V2 site’s behavior, but I pretty quickly realized my implementation of the site’s easter egg wasn’t going to be as straightforward as I wanted. I pushed its completion to be an aspect of my SLC state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 – Develop deployment process
&lt;/h3&gt;

&lt;p&gt;I’m going to go into more details about this process in a future entry so I’ll keep this section brief. For now, I’ll just say that I was able to develop this process much quicker and with less headaches than I was anticipating. DevOps is always a toss up for me. Production infrastructure causes me enough stress that I tend to over prepare in an attempt to lessen it. Through a combination of npm scripts, an additional repository, and a subdomain I was able to get something up and running in less than a day. It’s by no means the paragon of deployment processes, but it suits my needs well enough for this project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 – Push past stable to “SLC”
&lt;/h3&gt;

&lt;p&gt;Once I had successfully replaced my production site with a statically built and exported Next.js site, I got tests written/running and TypeScript written/building. All the while, I was refining my deployment process and pushing updates live. I was well on my way to SLC, but I realized I had a few more adaptations to make to my plans. Stepping back, I realized I’ll consider this project in a SLC state and Version 3 truely wrapped up when all these entries are published, and I’ve successfully added a site easter egg back into production. There’s work yet to be done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Previewing future entries in the series
&lt;/h2&gt;

&lt;p&gt;If writing these entries is now a necessary piece to consider this project &lt;em&gt;simple, loveable, and complete,&lt;/em&gt; what incremental deliverables do I have planned for this series? Of course this plan might not survive contact with reality either... but at the moment I’m planning on entries to cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Getting Styled Components (React) working with Next.js&lt;/li&gt;
&lt;li&gt;Getting Jest + React Testing Library working with Next.js&lt;/li&gt;
&lt;li&gt;Getting TypeScript working with Next.js&lt;/li&gt;
&lt;li&gt;Deploying Next.js to Github Pages&lt;/li&gt;
&lt;li&gt;Final thoughts and conclusions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve made it this far, thanks for reading! If you have comments, critiques, cautions, or clairvoyances please feel welcome to share them!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>githubpages</category>
      <category>react</category>
      <category>personalwebsite</category>
    </item>
  </channel>
</rss>
