<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Bren Henry</title>
    <link>https://www.bh3nry.click</link>
    <description>semi stream-of-consciousness blogs about software, philosophy, whatever</description>
    <atom:link href="https://www.bh3nry.click/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Not quite non-binary, not quite sure</title>
      <link>https://www.bh3nry.click/writing/gt-not-quite-nb-not-quite-sure</link>
      <guid isPermaLink="true">https://www.bh3nry.click/writing/gt-not-quite-nb-not-quite-sure</guid>
      <pubDate>Thu, 20 Aug 2026 12:37:50 GMT</pubDate>
      <category>Good Things</category>
      <content:encoded><![CDATA[<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong>Aug 18</strong></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">There is consistent tension in how much I let myself desire femininity. I rarely let go, but when I do, it re-enters with violent immediacy. Like punching a hole in your lungs and letting all the water rush in. The flip is usually accompanied by a few weeks of concerted effort towards achieving some sort of gender-euphoric acceptance. My evidence for believing myself is unfortunately the same evidence that invalidates it, and guarantees plausible deniability should I change my mind.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">All sorts of events have contributed to why I think this is who I am, or could be. Personifying myself as “her” is mostly done by feminizing myself through objects and association to said things. When I experience self-denial, I suppress any positive associations I had about presenting myself as “she” because it’s easier to just be a dude, or a gender-neutral person who happens to like objectively girly things.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The crux for me is that it seems obvious until it doesn’t, so I forget about it. Because I forget about it, new evidence that “I don’t want it as much as I think” scores itself against the version of me who just wants to let go.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong>June 9</strong></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">I got a nice haircut today. I’ve been somewhat indifferent about my gender expression lately, but getting a femme haircut was a lovely reminder of how modifying your identity sometimes only takes small votes of courage. These votes are sources of feedback for your brain and how you are perceived socially. Physical identity is proof staring you in the face, no questions asked, and you can use it as leverage in other areas of your life where you may need more confidence.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong>Aug 4</strong></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Read a great post after stumbling on <a href="https://blog.avas.space/whipping-girl/">Ava’s review of Whipping Girl</a>. Afterwards I read for about an hour straight on trans identity and how &quot;proof&quot; can only come from you.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">You decide. You set the conditions. If you believe yourself and your certainty comes from a place of love and understanding, it&#x27;s right. If there’s a sense of joy, perhaps a giddy feeling about the thought of being a girl, or a boy, it’s right.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">It’s easy to look back and see the signs; it’s easy to paint a narrative as well. I’m unsure of what to think, but I know that I love thinking of myself as her.</p>]]></content:encoded>
    </item>
<item>
      <title>Do I like what I think I like</title>
      <link>https://www.bh3nry.click/writing/gt-do-i-even-like-what-i-think-i-like</link>
      <guid isPermaLink="true">https://www.bh3nry.click/writing/gt-do-i-even-like-what-i-think-i-like</guid>
      <pubDate>Thu, 20 Aug 2026 12:37:50 GMT</pubDate>
      <category>Good Things</category>
      <content:encoded><![CDATA[<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong>June 10</strong></p>
<blockquote style="margin:2em 1em;padding-left:1em;border-left:3px solid #d0d7de;color:#73767a">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><em>“We have to accept human behaviour the way it is, not the way we would wish it to be.”</em></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><em>The Design of Everyday Things</em></p>
</blockquote>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">I feel as though it’s important to respond to randomness with curiosity. Whether behavioural or environmental, most things that happen are pretty random. Outcomes repeated are not necessarily any more frequent; rather, you prioritize thinking about them due to their familiarity.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">If I’m curious about my own behaviour, I could ask why I chose the response that I did. Directed at someone else, I could take interest in what I don&#x27;t understand about the other person. If I lived her life, would I do the same?</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Ultimately, we are in control of our behaviour at all times. Suppose someone cuts you off in traffic; if you were to answer the phone from your grandmother at the precise moment you felt angriest, would your engraged demeanor not immediately assume a more pleasant tone?</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Maybe the situation you’re facing is based on a decision made in the past. The decision at this point doesn’t exist, but its side effect does. While you are you, as the version you know yourself today, your past decisions will eventually knock at your window, asking if you still want to accept them.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Some decisions are nicer than others, and some will throw a rock through your window.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong>July 15</strong></p>
<blockquote style="margin:2em 1em;padding-left:1em;border-left:3px solid #d0d7de;color:#73767a">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><em>“Correcting a bias - Do the opposite of my instincts. If I tend to walk away, I choose to stay. When I notice I’m prejudiced against something, I choose to get to know it and appreciate it.”</em></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Excerpt From
Useful Not True
Derek Sivers</p>
</blockquote>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Lately I’ve been thinking about decisions and their hidden motivations. Good intentions that genuinely benefit other people may still exist to serve yourself. Extending grace towards another person for an action they feel guilty about could be a way of excusing your own selfish motivations.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">You might also want to do the thing they’re feeling bad about, but don&#x27;t want to admit it. If that&#x27;s the case, this is an opportunity to reflect and establish connection.</p>
<blockquote style="margin:2em 1em;padding-left:1em;border-left:3px solid #d0d7de;color:#73767a">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">“Hey, that thing you feel weird about, I also think about that stuff too, and we could probably navigate this together.”</p>
</blockquote>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">I had a great conversation last night that was both empowering and vulnerable. Things became far more interseting once the <em>&quot;I see you, just say what&#x27;s on your mind&quot;</em> blockers came down. Once we started digging into the thorns, everything was much lighter and more fulfilling.</p>
<h3 style="font-size:1.5rem;font-variant-ligatures:no-common-ligatures;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Practicing critical thinking before googling it</h3>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">A great post I read recently was about how to practice abstinence from search / research when faced with something you don’t know.
e.g., don’t turn to AI and instead reason in why an outcome is the way it is, then search and see how logical your thinking was.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong>July 22</strong></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Yesterday, I saw 4-5 baby owls. There&#x27;s a riverbed close by that looks like what I can only imagine the earth would have looked like in the Mesozoic era or something. Whenever I’m here, I tend to think about how if I died today, I’d be quite happy.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">I have been hyper-focused on pursuing my career these last two years; while very gratifying, I find my presence is both here and not at the same time. Every day I wake up and make decisions towards what I want to learn in a day. Is the majority of my time spent enjoying the things I think I enjoy? If not, what am I doing?</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Awareness is a finite, limited resource. The fewer things I have to consider, the better, generally. Physically, if I understand the cues from my body, whether emotionally or physiologically, I can choose to respond to them as they come up. However, this doesn’t mean I am automatically attuned to how much I am enjoying my life, and if this happened to be my last moment, is it a moment I’m grateful for?</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">I do think it is somewhat impossible to persist all of the mindsets that add value to your life at any one time. For example, sustained attention on learning needs space, and that space has to isolate you from certain things in order to achieve the benefit of you choosing to learn this thing.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">So what do we do? I believe that the answer is to know what trade-off you’re making at any given moment and regardless of the outcome, know that you made a choice <em>(from hopefully a higher state)</em> and if it doesn’t go your way, you’ve learned something about yourself and what you would choose to prioritize or do differently in the future.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">If you’re not making changes based on the feedback of your prior understandings of a decision, then you’re putting your effort on a treadmill.</p>]]></content:encoded>
    </item>
<item>
      <title>Deploying a containerized static site on Vercel</title>
      <link>https://www.bh3nry.click/writing/static-site-vercel-container-deployments</link>
      <guid isPermaLink="true">https://www.bh3nry.click/writing/static-site-vercel-container-deployments</guid>
      <pubDate>Tue, 11 Aug 2026 19:14:45 GMT</pubDate>
      <category>DevOps</category>
      <content:encoded><![CDATA[<link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/static-site-vercel-container-deployments/static-site-vercel-container-deployments-1.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/static-site-vercel-container-deployments/static-site-vercel-container-deployments-2.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/static-site-vercel-container-deployments/static-site-vercel-container-deployments-3.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/static-site-vercel-container-deployments/static-site-vercel-container-deployments-4.png"/><h2 style="font-size:2rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.4rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Nginx Config:</h2>
<h3 style="font-size:1.5rem;font-variant-ligatures:no-common-ligatures;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased"><em>Server block:</em></h3>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The configuration in this document is written to serve a containerized application with local port mapping on PORT <strong>:8080</strong> -- as well as <strong>:80</strong> when deployed to Vercel.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The following two lines specify where Nginx is going to serve your static web-app from.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><code>root /usr/share/nginx/html;</code>
<code>index index.html;</code></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In this example, I&#x27;ve pre-built a little web-app that is pre-rendering dynamic data at build time in order to achieve SSG. Afterwards, I just copied the build directory over to this new repo. To keep the scope of this write-up small, I won&#x27;t really get too deep on the build process.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><code>absolute_redirect off</code></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In a container-based application, your Nginx server is configured to manage incoming requests.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">If we had a multi-service architecture, we could setup Nginx for reverse-proxying and distribute requests internally to each of your containerized apps/services, <em>e.g., the front end and a backend.</em></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">When the <code>absolute_redirect</code> directive is turned <strong>OFF</strong>, Nginx will send back only the relative path  ----&gt; <code>/blogs/blog-post-1</code>  while letting the browser resolve the origin domain.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">e.g, Vercel.com/app-name/blogs/blog-post-1</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">As a default, Nginx is configured to have the <code>absolute_redirect</code> directive set to: <strong>ON</strong>.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">If we left it that way, there would be a few weird problems because Nginx will build a new URL out of three pieces of inferred information.</p>
<ul>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">The Scheme (http)</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Host Header (app-name.vercel.app)</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">The Port (8080)</li>
</ul>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Vercel terminates TLS at their edge network when requests are made to our container; so, internally Nginx would infer that the entire website must be HTTP.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Nginx will also append the port the URL --  Each time a user requests an end-point like <code>/blogs</code> or <code>/blogs/blog-post-1</code> - Nginx would re-direct the user to something like:</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><code>http://app-name.vercel.app:8080/blogs/blog-post-1</code></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">That URL doesn&#x27;t exist outside of the internal container environment and creates a strange user-experience.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<h3 style="font-size:1.5rem;font-variant-ligatures:no-common-ligatures;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Location Assets Block:</h3>
<h4 style="font-size:1.2rem;font-variant-ligatures:no-common-ligatures;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased"><em>Vercel &amp; Browser Cache</em></h4>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">As you would guess, a containerized app isn&#x27;t an effective way to utilize Vercel&#x27;s CDN. Normally if you weren&#x27;t trying to overly complicate your little static site like I am, you&#x27;d deploy directly on Vercel to take advantage of that built-in feature.</p>
<blockquote style="margin:2em 1em;padding-left:1em;border-left:3px solid #d0d7de;color:#73767a">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">*Vercel&#x27;s CDN caches your content (including pages, API responses, and static assets) in data centers around the world, closer to your users than your origin server.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">When someone requests cached content, Vercel serves it from the nearest region, cutting latency, reducing load on your origin, and making your site feel faster everywhere.*</p>
</blockquote>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In a container based app, Vercel routes all traffic to the container itself.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">To benefit from the Vercel CDN, we have to add some additional HTTP headers in the Nginx config file. Without these headers, users will experience cold-starts every time they visit our app, which basically means, the container will need time to boot up on each request.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Another detraction is that you would eventually hit a Vercel usage limit that throttles the entire account; which disrupts any other applications hosted on the platform. No bueno!</p>
<h5 style="font-size:1.1rem;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Compiled Javascript, CSS &amp; Image content</h5>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-nginx">location ^~ /assets/ {
        try_files $uri =404;
        add_header Cache-Control &quot;public, max-age=31536000, immutable&quot;;
        add_header Vercel-CDN-Cache-Control &quot;public, max-age=31536000&quot;;
    }
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Both headers in this block are set to have a max-age of one year.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">When you re-build your application, the <code>assets/</code> directory will generate brand new unique files. Even tho the user may have previous cache data that relates to an older file, it won&#x27;t matter as each subsequent build, distributes new content to the user untracked in their cache.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/static-site-vercel-container-deployments/static-site-vercel-container-deployments-1.png" alt="assets folder"/></p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<h3 style="font-size:1.5rem;font-variant-ligatures:no-common-ligatures;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Location  <code>/</code> Block:</h3>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><code>try_files $uri $uri/index.html =404;</code></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/static-site-vercel-container-deployments/static-site-vercel-container-deployments-2.png" alt="assets folder"/></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">If you look in your build directory, Nginx will match the <code>uri</code> to the directory name, and the <code>index.html</code> file at that level, else 404.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">On redeployments, Vercel wipes its CDN cache data, so on each deploy, a new version can be cached for the user until our next release, or the year has lapsed.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In the browser, we would want the user to be able to access our newest deployment, so we set the max-age to zero.</p>
<h5 style="font-size:1.1rem;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Website Routing / Pages</h5>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-nginx">location / {
        try_files $uri $uri/index.html =404;
        add_header Cache-Control &quot;public, max-age=0, must-revalidate&quot;;
        add_header Vercel-CDN-Cache-Control &quot;public, max-age=31536000&quot;;
    }
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The Browser cache will give users faster load times for contents/routes that haven&#x27;t changed. So while we may re-build and deploy this application periodically, the cache will remain consistent.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Even on a rollback, the cache is still sitting their dormant, so users who have previous experience with your app still benefit from faster load times.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<h4 style="font-size:1.2rem;font-variant-ligatures:no-common-ligatures;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Testing</h4>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">To validate that this worked, once you&#x27;ve deployed your app to Vercel, hit it with a curl request and watch it cache your deployment in real time.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><code>curl -sI https://vercel-container-ten.vercel.app/ | grep -i x-vercel-cache</code></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">It will respond back with MISS at first, and then HIT on the second request.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<h3 style="font-size:1.5rem;font-variant-ligatures:no-common-ligatures;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Location  <code>= /index.html</code> Block:</h3>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The last location block indicates that if someone tries to type in <code>index.html</code> literally in the search bar, that the app will default back to <code>/</code> route, which is what they would have wanted anyways.</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code>server {
    listen 8080;
    listen 80;
    server_name localhost;

    root /usr/share/nginx/html;
    index index.html;

    absolute_redirect off;

    location ^~ /assets/ {
        try_files $uri =404;
        add_header Cache-Control &quot;public, max-age=31536000, immutable&quot;;
        add_header Vercel-CDN-Cache-Control &quot;public, max-age=31536000&quot;;
    }

    location / {
        try_files $uri $uri/index.html =404;
        add_header Cache-Control &quot;public, max-age=0, must-revalidate&quot;;
        add_header Vercel-CDN-Cache-Control &quot;public, max-age=31536000&quot;;
    }

    location = /index.html {
        return 301 /;
    }
}

</code></pre>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<h2 style="font-size:2rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.4rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Docker Files</h2>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The Nginx config is the meat of this process, so let&#x27;s get into what you&#x27;re adding into your dockerfile(s), yes more than one.</p>
<ul>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Select your base image</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Copy the nginx conf file we just created into the assumed location that nginx expects</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Copy your pre-built app into the location that nginx serves content from</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Since it&#x27;s a container, expose it internally on port 8080</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Run <code>nginx -t</code>, which just checks for errors in your nginx config file.</li>
</ul>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-dockerfile">FROM nginx:1.27-alpine

COPY nginx.conf /etc/nginx/conf.d/default.conf

COPY build/client/ /usr/share/nginx/html/

EXPOSE 8080

RUN nginx -t
</code></pre>
<h2 style="font-size:2rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.4rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Vercel Dockerfile</h2>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In a Vercel deployment, you provide a second Dockerfile either called, <code>Containerfile.vercel</code>, or <code>Dockerfile.vercel</code> - I preferred container file to differentiate them more obviously.</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-dockerfile">FROM nginx:1.27-alpine

COPY nginx.conf /etc/nginx/conf.d/default.conf

COPY build/client/ /usr/share/nginx/html/

EXPOSE 80

RUN nginx -t
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">These are the exact same, you just expose Vercel on port 80.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<h2 style="font-size:2rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.4rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Deploy it!</h2>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">To deploy on Vercel, all you need to do is push this bad boy to Github and sync your repo with your Vercel account. That&#x27;s literally it.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Look at this tiny, beautiful, maintainable repo - chef&#x27;s kiss</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/static-site-vercel-container-deployments/static-site-vercel-container-deployments-3.png" alt="assets folder"/></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">and the Cache test.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/static-site-vercel-container-deployments/static-site-vercel-container-deployments-4.png" alt="assets folder"/></p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<h2 style="font-size:2rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.4rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Pre-built app</h2>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">To cover my bases, I wanted to showcase the react router config file as well so you can see how minimal it is. Surely you can serve a static site however you want, but I like React, and often I pre-build dynamic end-points before serving statically.</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-ts"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { <span class="hljs-title class_">Config</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@react-router/dev/config&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">appDirectory</span>: <span class="hljs-string">&quot;app&quot;</span>,
  <span class="hljs-attr">ssr</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-keyword">async</span> <span class="hljs-title function_">prerender</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&quot;http://localhost:3000/api/v1/birds&quot;</span>)
    <span class="hljs-keyword">const</span> <span class="hljs-attr">birds</span>: <span class="hljs-built_in">string</span>[] = <span class="hljs-keyword">await</span> res.<span class="hljs-title function_">json</span>()
    <span class="hljs-keyword">return</span> [<span class="hljs-string">&quot;/&quot;</span>, <span class="hljs-string">&quot;/birds&quot;</span>, ...birds.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">b</span>) =&gt;</span> <span class="hljs-string">`/birds/<span class="hljs-subst">${b.toLowerCase()}</span>`</span>)]
  },
} <span class="hljs-keyword">satisfies</span> <span class="hljs-title class_">Config</span>;
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">If this was an SSR app you&#x27;d probably have something like this:</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-ts"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { <span class="hljs-title class_">Config</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@react-router/dev/config&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">appDirectory</span>: <span class="hljs-string">&quot;app&quot;</span>,
  <span class="hljs-attr">ssr</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">routeDiscovery</span>: { <span class="hljs-attr">mode</span>: <span class="hljs-string">&quot;initial&quot;</span> },
  <span class="hljs-keyword">async</span> <span class="hljs-title function_">prerender</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&quot;http://localhost:3000/api/v1/birds&quot;</span>)
    <span class="hljs-keyword">const</span> <span class="hljs-attr">birds</span>: <span class="hljs-built_in">string</span>[] = <span class="hljs-keyword">await</span> res.<span class="hljs-title function_">json</span>()
    <span class="hljs-keyword">return</span> [<span class="hljs-string">&quot;/&quot;</span>, <span class="hljs-string">&quot;/birds&quot;</span>, ...birds.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">b</span>) =&gt;</span> <span class="hljs-string">`/birds/<span class="hljs-subst">${b.toLowerCase()}</span>`</span>)]
  },
} <span class="hljs-keyword">satisfies</span> <span class="hljs-title class_">Config</span>;
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Same-same but different. Flip SSR to true, and add the route discovery property if you want to load every route, asset all at once when the user visits your page. By default it&#x27;s set as &quot;lazy&quot; which for an SSR app just means that it loads each slug as it&#x27;s requested to maintain performance.</p>]]></content:encoded>
    </item>
<item>
      <title>Keeping track of user URL history</title>
      <link>https://www.bh3nry.click/writing/react-use-location-history</link>
      <guid isPermaLink="true">https://www.bh3nry.click/writing/react-use-location-history</guid>
      <pubDate>Sat, 08 Aug 2026 13:47:54 GMT</pubDate>
      <category>React</category>
      <content:encoded><![CDATA[<link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/react-use-location-history/react-use-location-history-1.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/react-use-location-history/react-use-location-history-2.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/react-use-location-history/react-use-location-history-3.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/react-use-location-history/react-use-location-history-4.png"/><p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Hey, did you know that the browser has native functionality that maintains the history of pages accessed by a user? As you build up your website, a small UX consideration you might want to make is tracking said URL history to improve the UI.</p>
<blockquote style="margin:2em 1em;padding-left:1em;border-left:3px solid #d0d7de;color:#73767a">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><a href="https://developer.mozilla.org/en-US/docs/Web/API/History_API">MDN History API</a></p>
</blockquote>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong>Scenario 1</strong>: Let&#x27;s say I mozy over to the log in page. Once logged in, maybe the app pushes me to the home page. Cool!</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong>Scenario 2</strong>: Now, let&#x27;s say I want to access a protected route. Once I&#x27;ve logged in, the app should boot me back to the route I was originally trying to access.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">To access the location aka URL metadata, React provides a built in hook called <code>useLocation</code></p>
<h3 style="font-size:1.5rem;font-variant-ligatures:no-common-ligatures;font-weight:700;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Usage</h3>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { useLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;react-router-dom&quot;</span>
</code></pre>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">const</span> location = <span class="hljs-title function_">useLocation</span>()
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Console logging the value of <code>location</code> provides you an object that looks something like this:</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-js">{<span class="hljs-attr">pathname</span>: <span class="hljs-string">&#x27;/home&#x27;</span>, <span class="hljs-attr">search</span>: <span class="hljs-string">&#x27;&#x27;</span>, <span class="hljs-attr">hash</span>: <span class="hljs-string">&#x27;&#x27;</span>, <span class="hljs-attr">state</span>: <span class="hljs-literal">null</span>, <span class="hljs-attr">key</span>: <span class="hljs-string">&#x27;default&#x27;</span>}
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The main thing we care about here are the <strong>pathname</strong> and <strong>state</strong> properties.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Conditionally, if a user hasn&#x27;t been authenticated, we can use/ask the <code>&lt;Navigate /&gt;</code> component to push them to the <code>/login</code> endpoint.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><code>&lt;Navigate /&gt;</code> accepts a <strong>state</strong> prop and works cohesively with a users location history. Because of this, we can create a link between the previous attempted path, and a login page.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In our Navigate component definition we create a state object with a new key called <em>&quot;prevLocation&quot;</em>, or whatever you want; and give it the value of our location path.</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">const</span> <span class="hljs-title function_">protectedRoute</span> = (<span class="hljs-params"></span>) =&gt; {
    <span class="hljs-keyword">const</span> isLoggedIn = <span class="hljs-variable language_">localStorage</span>.<span class="hljs-title function_">getItem</span>(<span class="hljs-string">&quot;loggedin&quot;</span>)
    <span class="hljs-keyword">const</span> location = <span class="hljs-title function_">useLocation</span>()

    <span class="hljs-keyword">if</span> (!isLoggedIn) {
        <span class="hljs-keyword">return</span> (
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Navigate</span> 
                <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>&quot;/<span class="hljs-attr">login</span>&quot; }
                <span class="hljs-attr">state</span>=<span class="hljs-string">{{</span>
                    <span class="hljs-attr">prevLocation:</span> <span class="hljs-attr">location.pathname</span>
                }} 
                <span class="hljs-attr">replace</span>
            /&gt;</span></span>)
    }
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Outlet</span> /&gt;</span></span>
}
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">After the user is forced to our logic screen, the metadata of location now looks like this:
<code>{pathname: &#x27;/login&#x27;, search: &#x27;&#x27;, hash: &#x27;&#x27;, state: {prevLocation: &#x27;/home&#x27;}, key: &#x27;er30evaf&#x27;}</code></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">If the user is logged in, then we&#x27;ll render all of the protected routes via the <code>&lt;Outlet /&gt;</code> component.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">On the login page, inside our authentication logic, we can specify that should any state exist in our path metadata, then boot the user back to that path, else just go back to the homepage.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Ignoring what an actual login component might have, a VERY simplified version of the location logic might look something like this:</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { useLocation, useNavigate } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;react-router-dom&quot;</span>
</code></pre>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">const</span> location = <span class="hljs-title function_">useLocation</span>()
<span class="hljs-keyword">const</span> navigate = <span class="hljs-title function_">useNavigate</span>()
</code></pre>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx">	<span class="hljs-title function_">navigate</span>(
	location.<span class="hljs-property">state</span> <span class="hljs-comment">// If there&#x27;s state aka `prevLocation`</span>
		? location.<span class="hljs-property">state</span>.<span class="hljs-property">prevLocation</span> <span class="hljs-comment">// go to the url they originally wanted</span>
		: <span class="hljs-string">&quot;/home&quot;</span>, { <span class="hljs-attr">replace</span>: <span class="hljs-literal">true</span> } <span class="hljs-comment">// else, just go back home</span>
	)}
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In this example we sub out the component version of <code>&lt;Navigate /&gt;</code> with the useNavigate hook. They are one in the same; all you need to know is that:</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">== <code>&lt;Navigate /&gt;</code> == - If you need automatic or conditional navigation during the render process <em>(e.g., redirection after successful login etc.).</em></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><mark style="background-color:#A9CCDE;color:inherit;padding:0.1em 0.15em;border-radius:2px">useNavigate</mark> - You need navigation to occur in response to user actions or events <em>(e.g., button click, form submission, or similar interactive events).</em></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">If you&#x27;re wondering what the <code>{ replace: true }</code> object is. Similar to a call stack, the browser gives us some built functionality called the History stack. <strong>replace = true</strong> just means that our navigate hook is going to replace the current location, which at this point would be <code>/login</code>, with the previous location ..</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">which would be something like:</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><code>/blog/secret-post-behind-auth-wall</code></p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<h2 style="font-size:2rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.4rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">User-flow</h2>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">User tries to get this end point</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/react-use-location-history/react-use-location-history-1.png" alt="url slug"/></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Is booted to the login</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/react-use-location-history/react-use-location-history-2.png" alt="auth deny"/></p>
<ul>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Previous path was <em><code>/home/secret</code></em></li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Current path after the Navigate push is <em><code>/login</code></em></li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">URL history now contains a state object with `prevLocation: &#x27;/home/secret&#x27;</li>
</ul>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/react-use-location-history/react-use-location-history-3.png" alt="console log with history obj"/></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">User is navigated to the original page after login</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/react-use-location-history/react-use-location-history-4.png" alt="happy path"/></p>]]></content:encoded>
    </item>
<item>
      <title>SSG is kinda confusing</title>
      <link>https://www.bh3nry.click/writing/ssg-in-react</link>
      <guid isPermaLink="true">https://www.bh3nry.click/writing/ssg-in-react</guid>
      <pubDate>Mon, 03 Aug 2026 03:03:41 GMT</pubDate>
      <category>React</category>
      <content:encoded><![CDATA[<link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/ssg-in-react/ssg-in-react-1.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/ssg-in-react/ssg-in-react-2.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/ssg-in-react/ssg-in-react-3.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/ssg-in-react/ssg-in-react-4.png"/><h2 style="font-size:2rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.4rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Thoughts on Static Site Generation in React</h2>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Suprisingly, static site generation (SSG) is somewhat counterintuitive for native React/React Router apps; why is this?</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Generally, a static site is quite easy to churn out. Like me, if you’re a year 1-ish developer, you&#x27;re probably working on a little portfolio proj.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">If you&#x27;re looking to pad your resume with simple get-to-know-me style site, save yourself some time and just use GH pages.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">For this site, that won&#x27;t really work, because:</p>
<ul>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">a) the project is a monorepo and relies on external data.</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">b) my experience maintaining the site matters, and I have specific requirements for how it is updated.</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">c) i want to use the patterns I learned in school and get practice working in larger codebases.</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">d) building a thing exactly to my spec, I would consider a form of self love! :)</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">e) doing hard stuff is fun.</li>
</ul>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Huge caveat; part of my frustration with SSG is probably self inflicted as I get closer to waving goodbye to this phase of the project.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><em>e.g., “if it’s not released, then it can’t be judged, and I can maintain a belief about myself that goes unchallenged/unseen”.</em></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">However, if you&#x27;re reading this, then that means I&#x27;ve most likely released it out into the world; which also means I&#x27;ve gotten over that weird internal belief and conquered the SSG behemoth.</p>
<h2 style="font-size:2rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.4rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Weird things</h2>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">My confusion is mostly re: Dynamic Routes and some discoveries I’ve had while exploring React’s nuances.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">I hoped (assumed) that React, out of the box, would provide an opportunity to perform SSG, and technically it does, if you use React Router… in <a href="https://reactrouter.com/start/mode">Framework mode</a>.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Over the summer I finished Scrimba&#x27;s React course - highly recommend. In that course, as well as college, the learning arc for React is that you build in Declarative mode, using BrowserRouter, Routes, Route etc.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The way React was described to me was that, it’s a declarative framework, just generally.. not that it’s declarative nature was a component of its Declarative modality.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In this new mode, all of the same rules, structures and patterns are more or less what you’re used to, but if you’ve already built an application in Declarative, it takes a fair bit of refactoring to adjust to Framework mode.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">I would appreciate if more introductory tutorials disclosed that React has different modes, because the fundamental patterns change a little bit when you alter the mode. Learning anything has a sweet spot for maintaining inertia and I found context switch here to be a pretty jarring.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Maybe the folks at Vite could build some of these questions into the <code>npm create vite@latest</code> command. Perhaps: <em>what are you building, does it have a backend?</em> etc - then have it select your mode for you.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Like most technical documentation, software people aren’t always writers, which means that there’s lots of documentation out there that doesn’t effectively speak to the reader.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">There are many options for SSG and most options don’t require that you pivot from Declarative mode. For me, I didn’t want to throw away the monorepo pattern and I’d like to gain more experience working on full stack applications. I also think it’d be advantageous if the codebase could resond to change. That way, I can practice a multitude of deployment strategies, on an architecture I’m familiar with.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Static site? Yep. Containerized app? Yep. I could easily convert and deploy this on an EC2 instance as well.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">There’s lots of language surrounding SSG and it seems most commonly recommended that you use Next.js for SSG as the framework is built for it. Which again, what the heck is Next.js?</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Coming from solely React, adopting another framework felt like a lot of overhead just to pass the build stage. Then I found that there are SSG frameworks like Astro. Which also seems very intriguing, but the same issue here. I just spent the last few months learning the ins and outs of React, to adopt a new framework?</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Anyway, if your situation is similar to mine and you’re looking to do React native SSG without adding another framework or library, then your app needs to be in Framework mode.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">I’ve landed on using React Router’s native SSG functionality and I’m sticking with it god dammit. In the refactor process, I did have to throw away a bunch of hooks and context wrappers. Which overall simplified my app, but I was a little sad to throw away.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">React Router allows you to pre-render whatever endpoint you want. When you build your app, all of the data fetched from the backend can also be included in your static build. A couple things need to happen to make this work.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">You need to use loaders. Loaders are essentially just React Router reserved functions that you use to fetch data. Whatever file fetches data, add a loader to the top of that file ‘loader’.</p>
<ol>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Inside the function, make a fetch request and return the data in whatever format you need for the component file you’re in.</p>
</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In the component definition add a parameter called loaderData. Inside your component above your return, assign the value to the loaderData.</p>
</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The loader is important because in your react router configuration file, it will look for a loader, and then go “ah, yes, a loader, I can see this data, I will preserve this data in your static build”.</p>
</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">You also need to make a fetch request in your pre render block, which seems a little redundant, as a pattern, but oh well. That’s ALMOST it. The rest is just getting your mind around the formatting of how Framework mode structures the route table, then writing your almost usual react code to build your pages.</p>
</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">When you’re building, make sure you run the backend server so that the data can be queried from the frontend during your build. Once it’s built, you can serve the build files on whatever platform you like.</p>
</li>
</ol>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Below is some technical documentation I’ve written on how you could implement a simple monorepo application using Express on the backend, with a React (Framework mode) frontend, that at build time pre-renders all dynamic routes into a zippy little static site.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong><code>react-router.config.ts</code></strong></p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { <span class="hljs-title class_">Config</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@react-router/dev/config&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">appDirectory</span>: <span class="hljs-string">&quot;app&quot;</span>,
  <span class="hljs-attr">ssr</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-keyword">async</span> <span class="hljs-title function_">prerender</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&quot;http://localhost:3000/api/v1/birds&quot;</span>)
    <span class="hljs-keyword">const</span> <span class="hljs-attr">birds</span>: <span class="hljs-built_in">string</span>[] = <span class="hljs-keyword">await</span> res.<span class="hljs-title function_">json</span>()
    <span class="hljs-keyword">return</span> [<span class="hljs-string">&quot;/&quot;</span>, <span class="hljs-string">&quot;/birds&quot;</span>, ...birds.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">b</span>) =&gt;</span> <span class="hljs-string">`/birds/<span class="hljs-subst">${b.toLowerCase()}</span>`</span>)]
  },
} <span class="hljs-keyword">satisfies</span> <span class="hljs-title class_">Config</span>;
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In Framework mode you&#x27;ll have a new file called <code>react-router.config.ts</code> - inside this file you specify your rendering strategy. Note that the above example assumes you&#x27;re working in a mono-repo, and the backend is fetching &quot;birds&quot; from somewhere else.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In the config, do three things:</p>
<ul>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Prefix a property called <strong>prerender()</strong> as an async fetch request to any endpoint(s) your Frontend needs from the Backend</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Set SSR to false</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">indicate the root directory of your frontend.</li>
</ul>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The simplest file structure you could have is something like this:</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code>frontend/
├── .react-router/
├── app/
│   ├── routes/
│   ├── app.css
│   ├── root.tsx
│   └── routes.ts
├── node_modules/
├── public/
├── .gitignore
├── package-lock.json
├── package.json
├── react-router.config.ts
├── README.md
├── tsconfig.json
└── vite.config.ts
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><em>Note: The backend would be at the same level as the frontend dir</em></p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In the return block, specify all of the routes you&#x27;d like to pre-render, which is likely all of them. The third route in my example is where your app may deviate from mine.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In this example, I&#x27;m dynamically generating slugs using data from the backend via the map method. The data being returned in this example is an Array with three values, so there&#x27;s nothing fancy going on here; however, your fetch request could look something like:</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">return</span> [<span class="hljs-string">&quot;/&quot;</span>, <span class="hljs-string">&quot;/birds&quot;</span>, ...birds.<span class="hljs-title function_">map</span>(<span class="hljs-function">(<span class="hljs-params">b</span>) =&gt;</span> <span class="hljs-string">`/birds/<span class="hljs-subst">${b.slug}</span>`</span>)]
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Dependent on the shape of your data coming in of course.</p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">I&#x27;m not going to explain the format of how Framework mode asks you to set up routes, but as an example, here&#x27;s a basic route hierarchy containing:</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">&quot;<code>/</code>&quot;<br/>
&quot;<code>/birds</code>&quot;
&quot;<code>/birds/:slug</code>&quot;  &lt;-------- (dynamic)</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong><code>routes.ts</code></strong></p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { <span class="hljs-keyword">type</span> <span class="hljs-title class_">RouteConfig</span>, index, prefix, route } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;@react-router/dev/routes&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> [
    <span class="hljs-title function_">index</span>(<span class="hljs-string">&quot;routes/home.tsx&quot;</span>),

    ...<span class="hljs-title function_">prefix</span>(<span class="hljs-string">&quot;birds&quot;</span>, [
        <span class="hljs-title function_">index</span>(<span class="hljs-string">&quot;routes/birds.tsx&quot;</span>),
        <span class="hljs-title function_">route</span>(<span class="hljs-string">&quot;:slug&quot;</span>, <span class="hljs-string">&quot;routes/birdslug.tsx&quot;</span>)
    ]),
    ] <span class="hljs-keyword">satisfies</span> <span class="hljs-title class_">RouteConfig</span>;
</code></pre>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">And by golly, we&#x27;ve made it to a component definition. This is your bread and butter right here. Notice the use of <code>loader</code> at the top.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The Birds component accepts <strong>loaderData</strong> which is equal to our backend response.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">**`birds.tsx</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">NavLink</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;react-router&quot;</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { <span class="hljs-title class_">Route</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./+types/birds&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title function_">loader</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params"></span>) =&gt; {
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&quot;http://localhost:3000/api/v1/birds&quot;</span>)
  <span class="hljs-keyword">const</span> <span class="hljs-attr">birds</span>: <span class="hljs-built_in">string</span>[] = <span class="hljs-keyword">await</span> res.<span class="hljs-title function_">json</span>()
  <span class="hljs-keyword">return</span> { birds }
}

<span class="hljs-keyword">const</span> <span class="hljs-title function_">Birds</span> = (<span class="hljs-params">{ loaderData }: <span class="hljs-title class_">Route</span>.<span class="hljs-title class_">ComponentProps</span></span>) =&gt; {
  <span class="hljs-keyword">const</span> { birds } = loaderData

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;bird-div&quot;</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Birds <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
                {birds.map((b) =&gt; (
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{b}</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">NavLink</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{b.toLowerCase()}</span>&gt;</span>
                            {b}
                        <span class="hljs-tag">&lt;/<span class="hljs-name">NavLink</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">Birds</span>
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Look at this beautiful app. Wow :&#x27;)</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong><code>/birds route in the browser</code></strong>
<img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/ssg-in-react/ssg-in-react-1.png" alt="landing page"/></p>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Here&#x27;s our dynamic page that renders a unique bird depending on what the user clicks on</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><strong><code>birdslug.tsx</code></strong></p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { <span class="hljs-title class_">Route</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./+types/birdslug&quot;</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> <span class="hljs-title function_">loader</span> = <span class="hljs-keyword">async</span> (<span class="hljs-params">{ params }: <span class="hljs-title class_">Route</span>.<span class="hljs-title class_">LoaderArgs</span></span>) =&gt; {
  <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">&quot;http://localhost:3000/api/v1/birds&quot;</span>);
  <span class="hljs-keyword">const</span> <span class="hljs-attr">birds</span>: <span class="hljs-built_in">string</span>[] = <span class="hljs-keyword">await</span> res.<span class="hljs-title function_">json</span>();
  <span class="hljs-keyword">const</span> bird = birds.<span class="hljs-title function_">find</span>(<span class="hljs-function">(<span class="hljs-params">b</span>) =&gt;</span> b.<span class="hljs-title function_">toLowerCase</span>() === params.<span class="hljs-property">slug</span>.<span class="hljs-title function_">toLowerCase</span>());

  <span class="hljs-keyword">if</span> (!bird) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Response</span>(<span class="hljs-string">&quot;Not Found&quot;</span>, { <span class="hljs-attr">status</span>: <span class="hljs-number">404</span> });

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

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">BirdSlug</span>(<span class="hljs-params">{ loaderData }: <span class="hljs-title class_">Route</span>.<span class="hljs-title class_">ComponentProps</span></span>) {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">&quot;bird-div&quot;</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{loaderData.bird}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The <code>params</code> parameter in this loader is just looking for the part of the URL that is <a href="https://reactrouter.com/start/framework/routing#dynamic-segments">dynamic</a>. In our case, it&#x27;ll will be the name of a bird e.g., &quot;bluejay&quot;. If that looks odd, don&#x27;t worry. Just pass the params parameter to all of your dynamic routes and write your logic however you see fit. I match the bird to the slug, but you can probably achieve this a zillion other ways.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The type import at the top is a form of type safety imposed by React Router.</p>
<blockquote style="margin:2em 1em;padding-left:1em;border-left:3px solid #d0d7de;color:#73767a">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><em>React Router generates types for each route in your app to provide type safety for the route module exports.</em></p>
</blockquote>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Again, not going to go into much detail here, as I myself am not very familiar with the intricacies of Type Safety in React Router, but if you don&#x27;t see these types anywhere, you&#x27;ll get a bunch of red squiggly&#x27;s. You can generate these types for your app via the <code>react-router typegen</code> CLI command.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><a href="https://reactrouter.com/explanation/type-safety#typegen-command">React Docs on the topic </a></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">**`/birds/:slug (bluejay) route in the browser
<img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/ssg-in-react/ssg-in-react-2.png" alt="dynamic path"/></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Cool, okay, so we&#x27;re ready to build our app.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Run the backend, then in a separate terminal, run your build command.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/ssg-in-react/ssg-in-react-3.png" alt="npm run dev via cli / express"/></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/ssg-in-react/ssg-in-react-4.png" alt="build command output"/></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In your frontend directory, if you want to inspect the contents of your build, run the npm run preview command, and voila.</p>]]></content:encoded>
    </item>
<item>
      <title>Interesting behaviours behind Javascript's sort() method</title>
      <link>https://www.bh3nry.click/writing/js-engine-sort-behaviours</link>
      <guid isPermaLink="true">https://www.bh3nry.click/writing/js-engine-sort-behaviours</guid>
      <pubDate>Tue, 14 Jul 2026 20:25:57 GMT</pubDate>
      <category>Web Dev</category>
      <content:encoded><![CDATA[<link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/js-engine-sort-behaviours/js-sort-image-01.png"/><link rel="preload" as="image" href="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/js-engine-sort-behaviours/js-sort-image-02.png"/><h1 style="font-size:2.5rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.5rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Javascript Sort() Behaviours</h1>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Something I learned today that I find interesting is how the <code>sort()</code> method in Javascript selects its sorting method at runtime. I was playing around on pythontutor.com while practicing the <em>&quot;is it an anagram?&quot;</em> leetcode challenge on Scrimba and noticed something odd.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">When you sort the string <em>&quot;convexsphere&quot;</em> is compares index 0 with index 11.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/js-engine-sort-behaviours/js-sort-image-01.png" alt="Initial comparison"/></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">However, if I sort the string <em>&quot;rosie&quot;</em> (my cat) it compares index 0 with index 1 first ... huh?</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="https://brenblogsite-mdxstorage-614302797491-us-east-2-an.s3.us-east-2.amazonaws.com/assets/js-engine-sort-behaviours/js-sort-image-02.png" alt="Second comparison"/></p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Apparently the sort sequence is chosen by the Javascript engine under the hood. In this example the sort method decided what characters to sort first based on the length of the array.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">HOWEVER, this isn&#x27;t accurate as of July 2026, because I checked what node version pythontutor.com was running and it returned v.6.0.0, which uses an older sorting alg that assesses sort order based on character length. In v6, smaller arrays used to default to an insertion sort, whereas longer arrays defaulted to quick-sort.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">None of this matters anymore because the most up-to-date JS engine uses something called Timsort, which scans the array to find small segments that are already sorted, and then a bunch of other more efficient oprerations.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">TimSort begins by examining the first two elements of the array, but after that I&#x27;m not entirely sure what it would do to the previous two examples.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Anyways. Fascinating stuff. I mainly wanted to look this up because I was curious what the values of <code>a</code>, and <code>b</code> were in the callback method. So, in case you ever wanted to know, the <code>a</code> and <code>b</code> in your callback are simply whichever two elements the engine has decided to compare at that moment.</p>]]></content:encoded>
    </item>
<item>
      <title>Render Props</title>
      <link>https://www.bh3nry.click/writing/render-props</link>
      <guid isPermaLink="true">https://www.bh3nry.click/writing/render-props</guid>
      <pubDate>Mon, 15 Jun 2026 13:24:28 GMT</pubDate>
      <category>React</category>
      <content:encoded><![CDATA[<link rel="preload" as="image" href="hierarchy-example-render-props.png"/><h2 style="font-size:2rem;font-variant-ligatures:no-common-ligatures;font-weight:100;font-family:Playfair Fallback;margin-top:1.4rem;margin-bottom:1.5rem;text-wrap:pretty;color:#241F19;-webkit-font-smoothing:antialiased">Visual Example</h2>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased"><img style="display:block;max-width:max(70%, min(100%, 20rem));height:auto;margin-block:1rem" src="hierarchy-example-render-props.png" alt="Excalidraw Diagram of a Render Prop hierarchy"/></p>
<blockquote style="margin:2em 1em;padding-left:1em;border-left:3px solid #d0d7de;color:#73767a">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">[!NOTE]  What&#x27;s happening??</p>
<ul>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased"><strong>SomeComponent</strong> passes a function as a prop called <code>doSomething</code>.</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Within the component definition <code>doSomething</code> is given a state value</li>
<li style="font-variant-ligatures:no-common-ligatures;font-family:arial;margin-top:1.2rem;padding:0;margin-inline:clamp(0px, 10vw - 1.5rem, 2rem);text-wrap:pretty;color:rgb(52, 50, 50);-webkit-font-smoothing:antialiased">Because the value is unknown at the top level, the component passes a value upwards in the hierarchy</li>
</ul>
</blockquote>
<hr style="border:none;height:1px;background-color:#e5e5e5;margin:2rem 0.5rem;max-width:740px"/>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">In the below example, the App level component is in charge of determining the <code>jsx</code> that gets rendered. Internally, the <code>Decision</code> component has a boolean state value that flips back and forth on an onClick event.</p>
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">When clicked the variable is passed to the render prop, which is passing a function. The function accepts a boolean and then renders conditional logic based on the true/false value. When clicked the page displays a different message.</p>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx"><span class="hljs-keyword">import</span> <span class="hljs-title class_">React</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;react&quot;</span>
<span class="hljs-keyword">import</span> <span class="hljs-title class_">Decision</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;./Decision&quot;</span>

<span class="hljs-keyword">function</span> <span class="hljs-title function_">App</span>(<span class="hljs-params"></span>) {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Decision</span> <span class="hljs-attr">render</span>=<span class="hljs-string">{(goingOut)</span> =&gt;</span> {
                return (
                    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>
                        Am I going out tonight?? {goingOut ? 
                        &quot;Yes!&quot; : &quot;Nope...&quot;}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
                )
            }} /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title class_">App</span>
</code></pre>
<pre style="max-width:calc(80ch + 3rem);margin-inline:clamp(0px, 5vw - 1.5rem, 1rem);min-width:0;margin-block:3rem;white-space:pre-wrap;overflow-wrap:anywhere;overflow-x:auto;border-radius:10px;padding:clamp(0.75rem, 3vw, 1.5rem);font-size:clamp(0.40rem, 0.5rem + 1vw, 0.9rem);line-height:1.5;tab-size:2;-webkit-font-smoothing:antialiased;border:1px solid #e5e5e5;background-color:#fafafa;color:#282525"><code class="hljs language-tsx">
<span class="hljs-keyword">import</span> <span class="hljs-title class_">React</span> <span class="hljs-keyword">from</span> <span class="hljs-string">&quot;react&quot;</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">Decision</span>(<span class="hljs-params">{ render }</span>) {
    <span class="hljs-keyword">const</span> [goingOut, setGoingOut] = <span class="hljs-title class_">React</span>.<span class="hljs-title function_">useState</span>(<span class="hljs-literal">false</span>)

    <span class="hljs-keyword">function</span> <span class="hljs-title function_">toggleGoingOut</span>(<span class="hljs-params"></span>) {
        <span class="hljs-title function_">setGoingOut</span>(<span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> !prev)
    }

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{toggleGoingOut}</span>&gt;</span>Change mind<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            {render(goingOut)}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}

</code></pre>
<blockquote style="margin:2em 1em;padding-left:1em;border-left:3px solid #d0d7de;color:#73767a">
<p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">The rendered page:</p>
</blockquote>
<video controls="" width="100%"><source src="screen-recording-example.mov" type="video/quicktime"/><p style="text-wrap:pretty;font-size:15pt;font-family:Playfair;text-align-last:left;hyphens:auto;margin-bottom:1rem;margin-top:1rem;color:rgb(0, 0, 0);-webkit-font-smoothing:antialiased">Your browser does not support the video tag.</p></video>]]></content:encoded>
    </item>
  </channel>
</rss>