<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[freddydumont's blog]]></title><description><![CDATA[Tips and articles about Front-End development with TypeScript, React, Node.js, GraphQL and more!]]></description><link>https://freddydumont.com/blog/</link><generator>GatsbyJS</generator><lastBuildDate>Fri, 27 Jun 2025 22:25:32 GMT</lastBuildDate><item><title><![CDATA[How to add Custom Locations in Nginx Proxy Manager]]></title><description><![CDATA[Setting up custom locations in Nginx Proxy Manager isn't obvious, especially for WebSockets. Here's how to do it right.]]></description><link>https://freddydumont.com/blog/npm-custom-locations</link><guid isPermaLink="false">https://freddydumont.com/blog/npm-custom-locations</guid><pubDate>Fri, 11 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After installing Coolify on my homelab and proxying it with Nginx Proxy Manager (NPM), I ran into an issue. Even with WebSockets Support enabled, I kept getting warnings in the UI and browser console about failed WebSocket connections.&lt;/p&gt;
&lt;p&gt;The problem was that Coolify uses different routes and ports for WebSockets, and configuring NPM to handle this correctly isn’t obvious.&lt;/p&gt;
&lt;p&gt;Here’s how to properly set up custom locations in NPM when you need them:&lt;/p&gt;
&lt;h2&gt;Basic Setup&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;First, create your proxy host with the main website configuration&lt;/li&gt;
&lt;li&gt;Enable WebSockets in the main configuration if needed&lt;/li&gt;
&lt;li&gt;Add custom locations for specific paths requiring special handling&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;mx_auto&quot; style=&quot;max-width: 400px;&quot;&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/d58fdcae3d0bd9d2f03fefdeff8a446d/00d43/npm-host%402x.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 109.375%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACkElEQVR42p1USVLbQBTViXKbnDDbrHIANuxSBUmoDJABg/GAxtbQLbXGl/cbSxaxgSJNPdp2/+H90UvTFHGSQCkFxc8Jb2st6rp+FURHbHlxorC8W2O12WJNbLb36Pserz1t20JrDa/tuqMCwzAQ2GFwTub3XmaYvpdlCS/TNfzUIspr56UoDLQp+VjRo0FZWadgTMX3DhVDExlDmYpv8yM6nq1b2GZA3e69dmTRz+CY7TCy7GZvI0NjDLw0y7Bab1z+BCbP8b/HhSzVKavKhSJ3x5yKP8dux2b8PLIZ2c4jmAxKXg6LMTxTqMP3KeSSIReFxvbeR84eGh80c+EHIcIoxtYPEIQR4jhBzaTLkWjkTX4Xua5p9gwN/0UUNuyh0aAoSH8K5E0aPsty18ByGhqIOQCJSiE1EIN9P4wht06hadqJvuRFBGWCCjpq2vZReJK/hDrSOtjlW+sSQs6rab2ksZrFEDRdj1YSLcoivGO9MAV+69yxqevGORFda2sHceT6sGUOG5WjJP0iSpAFMYzKQC0MFBwNnqoQp5GPws37w8wLJLdhFDmWlaXBi/MznPy5grm9Q7a4hV6uUNwukd4sYb7+kIQdVPOpU7HtvJ8XX3C2XMCuNkivb5DTaL5YQvGuvl1NBgf3h6MtMxVTDBpWMiS7zd0Klu0C2TSyMJijgbkdc/kSJoOWDFRRQHHktCwE/qhZrUl41szPYTYpLfK8eOg1Gk3TzC3Kx76Pzs2ETlrJTcqR0Ts2Zi9jtr6koft/1tV8LT2FueylzvBJK2w4DF73xMZ+6chy9TnnOcN8e/kRbz6f4P16AS8IAs5rjIjNGYYh5Lu7idAthfgAIutTzoFyH66v8O7Xd5zfXOMv0YSymBfuOLgAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;NPM basic host settings&quot; title=&quot;&quot; src=&quot;/static/d58fdcae3d0bd9d2f03fefdeff8a446d/e5715/npm-host%402x.png&quot; srcset=&quot;/static/d58fdcae3d0bd9d2f03fefdeff8a446d/8514f/npm-host%402x.png 192w,
/static/d58fdcae3d0bd9d2f03fefdeff8a446d/804b2/npm-host%402x.png 384w,
/static/d58fdcae3d0bd9d2f03fefdeff8a446d/e5715/npm-host%402x.png 768w,
/static/d58fdcae3d0bd9d2f03fefdeff8a446d/00d43/npm-host%402x.png 1000w&quot; sizes=&quot;(max-width: 768px) 100vw, 768px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;/div&gt;
&lt;h2&gt;Adding WebSocket Custom Locations&lt;/h2&gt;
&lt;p&gt;From your proxy host details page, click “Custom Locations” and then “Add Location” :&lt;/p&gt;
&lt;div class=&quot;mx_auto&quot; style=&quot;max-width: 400px;&quot;&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/1638b0c5b1b63e57b0a57f7020065d5b/00d43/npm-custom-locations%402x.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 152.60416666666669%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAfCAYAAADnTu3OAAAACXBIWXMAAAsTAAALEwEAmpwYAAACzUlEQVR42s1VWW7bMBD1VXqwnqo36CmKnqAfzV9/ktS1ZcmO9s1aSJHi6wy1WHEcxA5SoAQeKInU4yxvhqs8z5GkKbIsQ0bPDCnlzRBCoCxLrIIoguN6cL0DvP0BhycfxhjcOpRSqOsaK631xQ1LUn7u+36czbzG8wQebOWqacnUukUjJFplEJUKSdkhqzpEBc/K/lA3rbWipX3HqiZrWrT073Kw66uOYtYfKxiCPtLGqEB1FKhlj7rVdJCeLZygGXqYX1go1xsUFLti5yL39pA7D70fwgw+3RRHKYlQbF2ERBg6LnwiFE8BjB9MwXubhfeM+6zL8omsyQogLy1MnMJQvPqzoL8K2mf3Ti5z1sqqgp8kiPMCYZpBNM11PrJx5REocqCpR0KlKWMNgjBCGESIk5QyKWaZXASvsWVEVjzu4G1jVGsXIssHHXLGkjS3ZB0dwNLoSCJ4i5AIYjfGfdIj34WQcTIQsttK6UEW40+zREZ5TJjW7Dt5IjcupLOH3vsQXClMllHs/mwdZHFsQ8OH7CjjDklp6+yw3mztexQE1uqUtLvlNSrZwKdSHauNhT5aSCYXBZTWC9cu5sCusTcVWTOV35EKgjlsUiaXKyonPRKy2xzDEzrIrhviOnogSHMTYUdrbIAl5NPOB5/GbSwlCRVFiZRaW0kZlSPJa2MmNBc6jZTd0OvodEFZ5+Sc7zGLMMyEmrM7Ewxgd4RtnJ2VkFh8n2Z2/7xiThbSh0kWp26iR9kMOl12muHZvEjYM8KPGP83ofnHhHhx6VwLHp29a4SVl62UlrJWamXl8y7rRvK59JpeI+oklOk/ICn6FMN+rONbochDRXMzWNjbGPZjh3kPnt3LfHkv79xrMVTP0L1/VyXuygxeWXCD7d8ZL4kDNVdBN+Tnhzt8+vkdX5x7rMIwREyd+laEdKn5QYiI5m+bR3x9+IUfmzX+ApsNgCy3CptzAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;NPM custom locations&quot; title=&quot;&quot; src=&quot;/static/1638b0c5b1b63e57b0a57f7020065d5b/e5715/npm-custom-locations%402x.png&quot; srcset=&quot;/static/1638b0c5b1b63e57b0a57f7020065d5b/8514f/npm-custom-locations%402x.png 192w,
/static/1638b0c5b1b63e57b0a57f7020065d5b/804b2/npm-custom-locations%402x.png 384w,
/static/1638b0c5b1b63e57b0a57f7020065d5b/e5715/npm-custom-locations%402x.png 768w,
/static/1638b0c5b1b63e57b0a57f7020065d5b/00d43/npm-custom-locations%402x.png 1000w&quot; sizes=&quot;(max-width: 768px) 100vw, 768px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;/div&gt;
&lt;h3&gt;Configure the Basic Fields&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Location&lt;/strong&gt;: Enter the path (e.g., &lt;code&gt;/app&lt;/code&gt; or &lt;code&gt;/terminal/ws&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scheme&lt;/strong&gt;: Select &lt;code&gt;http&lt;/code&gt; (or &lt;code&gt;https&lt;/code&gt; if your backend uses SSL)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forward Hostname / IP&lt;/strong&gt;: Enter the IP address &lt;strong&gt;only&lt;/strong&gt; (e.g., &lt;code&gt;192.168.1.12&lt;/code&gt;)
&lt;ul&gt;
&lt;li&gt;that is, without the path used in location&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forward Port&lt;/strong&gt;: Enter the specific port for this path (e.g., &lt;code&gt;6001&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Configure Advanced Settings&lt;/h3&gt;
&lt;p&gt;In the text area that shows up after clicking the gear icon, you &lt;strong&gt;must&lt;/strong&gt; include the complete location block configuration.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;nginx&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; /your-path &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_pass&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; http://your-ip:your-port&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_http_version&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; 1.1&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_set_header&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; Upgrade &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;$http_upgrade&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_set_header&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; Connection &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;Upgrade&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_set_header&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; Host &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_read_timeout&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;86400&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’d expect the &lt;code&gt;textarea&lt;/code&gt;’s contents to be inserted in the block generated from the mandatory inputs, but that won’t work. Don’t ask me why.&lt;/p&gt;
&lt;h2&gt;Coolify example&lt;/h2&gt;
&lt;p&gt;Coolify has two different WebSocket paths:&lt;/p&gt;
&lt;h3&gt;For &lt;code&gt;/app&lt;/code&gt; WebSocket:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Location&lt;/strong&gt;: &lt;code&gt;/app&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scheme&lt;/strong&gt;: &lt;code&gt;http&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forward IP&lt;/strong&gt;: &lt;code&gt;192.168.1.12&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Port&lt;/strong&gt;: &lt;code&gt;6001&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced Config&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;nginx&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; /app &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_pass&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; http://&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;192.168.1.12:6001&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_http_version&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; 1.1&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_set_header&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; Upgrade &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;$http_upgrade&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_set_header&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; Connection &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;Upgrade&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_set_header&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; Host &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_read_timeout&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;86400&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;For &lt;code&gt;/terminal/ws&lt;/code&gt; WebSocket:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Location&lt;/strong&gt;: &lt;code&gt;/terminal/ws&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scheme&lt;/strong&gt;: &lt;code&gt;http&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forward IP&lt;/strong&gt;: &lt;code&gt;192.168.1.12&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Port&lt;/strong&gt;: &lt;code&gt;6002&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Advanced Config&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;nginx&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; /terminal/ws &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_pass&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; http://&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;192.168.1.12:6002&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_http_version&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; 1.1&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_set_header&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; Upgrade &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;$http_upgrade&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_set_header&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; Connection &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;Upgrade&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_set_header&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; Host &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;$host&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;proxy_read_timeout&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;86400&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Troubleshooting&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;If your proxy shows offline, check the generated Nginx configuration. You’ll find them under &lt;code&gt;/data/nginx/proxy_host&lt;/code&gt;. Numbers will match what’s in the UI.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;mx_auto&quot; style=&quot;max-width: 240px;&quot;&gt;
  &lt;span class=&quot;gatsby-resp-image-wrapper&quot; style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 456px; &quot;&gt;
      &lt;a class=&quot;gatsby-resp-image-link&quot; href=&quot;/static/b8df645295b5387b725a7a25ba4fdca0/7f664/nginx-host-number%402x.png&quot; style=&quot;display: block&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;
    &lt;span class=&quot;gatsby-resp-image-background-image&quot; style=&quot;padding-bottom: 98.95833333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAACXBIWXMAAAsTAAALEwEAmpwYAAABxklEQVR42q2U6VaDMBCFff/H8UX8Y9WjVtvShZYlkLZA1uskAWyrFvTIYdokJN9sF260Mfgva6TCDf7xkkoHoDICxmgIISHJi9Ya1tpRELev29sD75JbJDzC08ML7icPmDw+Ic/zcMBYd+pHoFLKB9IDDR0QVkHBUqQ0ploIbSAJomlTb9fqR/t7oPNgyj1syWH5HiDz//uwBs5hWAGr9c9Rts88UBQl5HSGLFohm83Bl2uwxRLJ2wxsHmE3W6B+fPaO2qJ924y+y6rg0AQ4Ejhfx8jWG/BdgpTAfJuAZwyCHF4DnkWopIROUiDLYVsDY94srbu5ibew5H1Uyq6grpNxyrA/1mQVVnEKVoaIxojnAkiao/yTNEddN6gccL1FQaXoUxzQ5HnKDngh0FPR4rcRdpNdkuNY1T7S9/kS0/cIr28LpNSUIfi5bFQotlO7S1/Rokv9cKhQVQ2ElKPe4U/Z6DBx/s1F2l0ZzIB9Sdk/IKixpoV8eu+cXLNzoAqTaLlBSeIt+cGPF6sN4l3qHf2pKQ7UUB3rpiGNF74ZjJWjOv1F2F7cbdoeEO5QV1eKAdOnQPcjZPimeTsdd/MB8x9lp45G4gPckSQwTSn+MAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;&gt;&lt;/span&gt;
  &lt;img class=&quot;gatsby-resp-image-image&quot; alt=&quot;NPM UI with host number&quot; title=&quot;&quot; src=&quot;/static/b8df645295b5387b725a7a25ba4fdca0/7f664/nginx-host-number%402x.png&quot; srcset=&quot;/static/b8df645295b5387b725a7a25ba4fdca0/8514f/nginx-host-number%402x.png 192w,
/static/b8df645295b5387b725a7a25ba4fdca0/804b2/nginx-host-number%402x.png 384w,
/static/b8df645295b5387b725a7a25ba4fdca0/7f664/nginx-host-number%402x.png 456w&quot; sizes=&quot;(max-width: 456px) 100vw, 456px&quot; style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;
  &lt;/a&gt;
    &lt;/span&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Check your firewall configuration with &lt;code&gt;ss -tuln&lt;/code&gt;. The application ports you’re trying to proxy should be in the list.&lt;/li&gt;
&lt;li&gt;Ensure that the location path in the Advanced tab exactly matches the Location field&lt;/li&gt;
&lt;/ul&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-11 { color: #C838C6; }
  .eva-light-bold .grvsc-tldFB4-13 { color: #626264; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-6 { color: #FF6D12; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-12 { color: #CF68E1; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-7 { color: #FF9070; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[Generate D2 PNG diagrams in GitHub Actions]]></title><description><![CDATA[Automatically generate and publish D2 diagram PNGs using GitHub Actions.]]></description><link>https://freddydumont.com/blog/d2-github-actions</link><guid isPermaLink="false">https://freddydumont.com/blog/d2-github-actions</guid><pubDate>Fri, 10 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://d2lang.com/&quot;&gt;D2&lt;/a&gt; is a cool little DSL that allows you to generate diagrams from code.&lt;/p&gt;
&lt;p&gt;It’s quite easy to learn, and can be a much better option for developers than using drag-and-drop diagramming software.&lt;/p&gt;
&lt;p&gt;Running it locally is simple: &lt;code&gt;d2 my-diagram.d2&lt;/code&gt; will generate a SVG of your diagram.&lt;/p&gt;
&lt;p&gt;However, if your D2 code embeds remote images, those links will be embedded in the SVGs, so those images will not show in contexts where doing that isn’t allowed.&lt;/p&gt;
&lt;p&gt;While you can generate a PNG locally, &lt;code&gt;d2&lt;/code&gt; relies on &lt;a href=&quot;https://playwright.dev/&quot;&gt;&lt;code&gt;playwright&lt;/code&gt;&lt;/a&gt; to take a screenshot of the SVG.&lt;/p&gt;
&lt;p&gt;For this reason, or any other, you might want to build your diagrams in CI using GitHub Actions.&lt;/p&gt;
&lt;p&gt;Here’s a workflow that does just that, commented for clarity:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;yaml&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;Generate PNG diagrams&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Triggers on new version tags with filename suffix, e.g., dns.d2 -&amp;gt; v1.2.3-dns&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;v*.*.*-*&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; softprops/action-gh-release@v1 needs these permissions&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;write&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;actions/checkout@v4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Extracts the file name from the pushed tag&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Extract filename&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;echo &amp;quot;FILENAME=${GITHUB_REF#refs/tags/*-}&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Install d2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;curl -fsSL https://d2lang.com/install.sh | sh -s --&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Caches playwright without relying on `package.json`.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; If you want to force a playwright upgrade, delete the cache in the GitHub Actions UI.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Cache playwright binaries&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;cache-playwright&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;actions/cache@v4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;~/.cache/ms-playwright&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;playwright-cache&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Installs playwright if it&amp;#39;s not already cached&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Install playwright&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;steps.cache-playwright.outputs.cache-hit != &amp;#39;true&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;npx playwright install --with-deps chromium&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Assumes the diagram is located at the repo&amp;#39;s root.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Adjust according to your project structure.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Generate diagram&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;d2 ${{ env.FILENAME }}.d2 ${{ env.FILENAME }}.png&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Publishes the PNG under GitHub&amp;#39;s Release tab&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Release&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;softprops/action-gh-release@v1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;${{ env.FILENAME }}.png&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using this basic workflow, your PNG diagrams will be available under the Release tab in GitHub.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-i { font-style: italic; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-17 { color: #8E8E90; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-11 { color: #C838C6; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-i { font-style: italic; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-17 { color: #8E99B1; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-12 { color: #CF68E1; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[How to generate NixOS LXC templates in GitHub Actions]]></title><description><![CDATA[Run NixOS builds in CI using GitHub Actions and nixos-generators to get ready-to-use LXC templates for Proxmox and other virtualisation platforms.]]></description><link>https://freddydumont.com/blog/nixos-github-actions</link><guid isPermaLink="false">https://freddydumont.com/blog/nixos-github-actions</guid><pubDate>Sat, 23 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I started using Nix some time ago when I got this M2 MacBook Air. With &lt;code&gt;nix-darwin&lt;/code&gt; and &lt;code&gt;home-manager&lt;/code&gt;, it’s a great way to handle dotfiles.&lt;/p&gt;
&lt;p&gt;The thing with Nix is, when you start using it somewhere, you start using it everywhere. There is no turning back to entering commands one after the other to install and configure programs.&lt;/p&gt;
&lt;p&gt;Naturally, when I started homelabbing with Proxmox (PVE) as my hypervisor, I used NixOS to create LXC containers, which are bootstrapped using a template.&lt;/p&gt;
&lt;p&gt;While you could pull a NixOS container template from &lt;a href=&quot;https://hydra.nixos.org/job/nixos/trunk-combined/nixos.containerTarball.x86_64-linux&quot;&gt;Hydra&lt;/a&gt;, install it in PVE, and pull your configuration from your Nix config repo, what if you could instead generate a pre-built NixOS LXC template direclty from you repo?&lt;/p&gt;
&lt;p&gt;That’s exactly what I ended up doing, using GitHub Actions and &lt;a href=&quot;https://github.com/nix-community/nixos-generators&quot;&gt;&lt;code&gt;nixos-generators&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Here’s my GitHub Actions workflow to generate NixOS container templates. I’ve commented for clarity.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;yml&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;generate-lxc&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; The workflow is triggered on new version tags with a hostname suffix,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; where the hostname corresponds to a nixosConfiguration in flake.nix.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;v*.*.*-*&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; softprops/action-gh-release@v1 needs these permissions&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;contents&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;write&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;actions/checkout@v4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Extract the hostname from the version tag suffix and&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; save it as an environment variable.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Extract tag suffix&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;echo &amp;quot;TAG_SUFFIX=${GITHUB_REF#refs/tags/*-}&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Install nix&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;cachix/install-nix-action@v24&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;github_access_token&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Use the tag suffix to specify which configuration to build with the generator,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; and then store the build path in an environment variable.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Generate NixOS configuration&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;|&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;          nix run github:nix-community/nixos-generators -- -f proxmox-lxc --flake .#${{ env.TAG_SUFFIX }} | {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;            read path&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;            echo &amp;quot;BUILD_PATH=$path&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;          }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Move the build artifact to a working directory and&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; rename it to include the tag suffix.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Prepend tag suffix to file name&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;|&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;          NEW_FILENAME=&amp;quot;${{ env.TAG_SUFFIX }}-$(basename ${{ env.BUILD_PATH }})&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;          RELEASE_PATH=&amp;quot;${{ github.workspace }}/$NEW_FILENAME&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;          cp &amp;quot;${{ env.BUILD_PATH }}&amp;quot; &amp;quot;$RELEASE_PATH&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;          echo &amp;quot;RELEASE_PATH=$RELEASE_PATH&amp;quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Create a GitHub release and attach the generated container template.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;Release&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;softprops/action-gh-release@v1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;${{ env.RELEASE_PATH }}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This assumes you’re using flakes, and have a &lt;code&gt;flake.nix&lt;/code&gt; file at the root of your repo.&lt;/p&gt;
&lt;p&gt;Here’s what &lt;code&gt;nixosConfigurations&lt;/code&gt; should look like:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;nix&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; { &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;darwin&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;home-manager&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;nixpkgs&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;... &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;}: {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;darwinConfigurations&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;# macOS config&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    }&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;nixosConfigurations&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;nfs&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;nixpkgs&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;nixosSystem&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;x86_64-linux&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; [&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;nixpkgs&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;/nixos/modules/virtualisation/proxmox-lxc.nix&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;./nixos&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;./nixos/nfs.nix&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        ]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      }&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;dns&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;nixpkgs&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;nixosSystem&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;x86_64-linux&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; [&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;nixpkgs&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;/nixos/modules/virtualisation/proxmox-lxc.nix&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;./nixos&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;./nixos/dns.nix&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        ]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      }&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;# ... other configurations&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    }&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  }&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For instance, if I want to build the &lt;code&gt;nfs&lt;/code&gt; system, I commit and push my changes, then tag it as &lt;code&gt;v0.2.2-nfs&lt;/code&gt;. For &lt;code&gt;dns&lt;/code&gt;, I would tag it &lt;code&gt;v0.1.0-dns&lt;/code&gt;. Here’s what the releases page look like:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/37b542c5309ba2ea912338f3fc30a532/23d36/nixos-releases.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 63.020833333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAABYlAAAWJQFJUiTwAAABUUlEQVR42pVTi3KCMBDk/7+xUytqiw8ECeSdrLmLUEbR2szcMAmXvb3dSxFCQIwRtOjrvIe/ndHpW5Fy6Q6tYrPdQmnNG6kUaL/6WmN/PELYYSr2alGOtRaUWpSbHS6d4B8+sRuGAcaYVNEjxFx1idWz84LAtDacQO2WuwpnodCpgIsM6LXP7d+FMRbOuXzPeRhmGFHMqZMOP43G+uRwEg7aBigTFgFZ6xQslZRo2ktmOAe0qWLdKdS9w2DCr0Z/tEz3SP8HwHvH/xWjhlVVMfroMpn0uVrj3LS5yLPxmQEREZ2MZMCy3KAT/WRK27asyajPOy1PhWls6nOLQappbPaHGkIaKBtZR+3CIuD4AEYzac+Ac93oR3mU+DhYfDcujY2HUM/Hxto8NqLvWaIHU7hlodEmIMkuZz5LgHPj6JXQLJOiD4Dh1ka8aRJfmbLwUq4pSwDlh+Bp8AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;GitHub repo releases page&quot;
        title=&quot;&quot;
        src=&quot;/static/37b542c5309ba2ea912338f3fc30a532/e5715/nixos-releases.png&quot;
        srcset=&quot;/static/37b542c5309ba2ea912338f3fc30a532/8514f/nixos-releases.png 192w,
/static/37b542c5309ba2ea912338f3fc30a532/804b2/nixos-releases.png 384w,
/static/37b542c5309ba2ea912338f3fc30a532/e5715/nixos-releases.png 768w,
/static/37b542c5309ba2ea912338f3fc30a532/4ad3a/nixos-releases.png 1152w,
/static/37b542c5309ba2ea912338f3fc30a532/71c1d/nixos-releases.png 1536w,
/static/37b542c5309ba2ea912338f3fc30a532/23d36/nixos-releases.png 2354w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;If your repo is public, you can then use the &lt;code&gt;Download from URL&lt;/code&gt; button in the PVE user interface and give it the tarball URL direclty. Otherwise, download it locally and upload it to PVE manually.&lt;/p&gt;
&lt;p&gt;And that’s it! When you create a Linux container using this template, you’ll have a NixOS machine ready to go!&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-i { font-style: italic; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-17 { color: #8E8E90; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-11 { color: #C838C6; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-7 { color: #00BEC4; }
  .eva-light-bold .grvsc-tldFB4-14 { color: #F0AA0B; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-i { font-style: italic; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-17 { color: #8E99B1; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-12 { color: #CF68E1; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-8 { color: #56B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-14 { color: #E4BF7F; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[How to configure Vite to include additional folders]]></title><description><![CDATA[What to do if a folder isn't picked up by Vite. How to explicitely tell Vite to include a specific folder in its bundle. Resolve aliases are the solution.]]></description><link>https://freddydumont.com/blog/vite-add-folder</link><guid isPermaLink="false">https://freddydumont.com/blog/vite-add-folder</guid><pubDate>Sat, 02 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When setting up &lt;a href=&quot;https://panda-css.com/&quot;&gt;Panda CSS&lt;/a&gt; in a RedwoodJS project, I ran into a small issue: although autocomplete worked fine in my editor, the dev server couldn’t find the generated Panda files, so the styles weren’t applied.&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;The solution is quite simple, if you know that the keyword to search for is &lt;code&gt;resolve&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;I ended up finding it in this &lt;a href=&quot;https://stackoverflow.com/a/73742188&quot;&gt;StackOverflow answer&lt;/a&gt;. Note that the accepted answer is out of date and only adds unnecessary steps.&lt;/p&gt;
&lt;p&gt;All you need to do is add the following highlighted lines to your Vite config.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container grvsc-has-line-highlighting eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;ts&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;viteConfig&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;UserConfig&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;redwood&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;styled-system&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;replacement&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;__dirname&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;styled-system&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells Vite to find imports starting with &lt;code&gt;styled-system&lt;/code&gt;, in the same folder as Vite’s config.&lt;/p&gt;
&lt;h2&gt;Explanation&lt;/h2&gt;
&lt;p&gt;By default, Vite’s entry point in a RedwoodJS project is in &lt;code&gt;web/src&lt;/code&gt;, so the bundler has no awareness of what’s at the root level (&lt;code&gt;web&lt;/code&gt;). The &lt;code&gt;resolve&lt;/code&gt; alias maps &lt;code&gt;styled-system&lt;/code&gt; imports to the correct location, fixing the issue.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-16 { color: #7C4DFF; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-17 { color: #8E8E90; }
  .eva-light-bold .grvsc-tldFB4-7 { color: #00BEC4; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-3 { color: #437AED; }
  .eva-light-bold .grvsc-tldFB4-5 { color: #A0A1A7F2; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-16 { color: #A78CFA; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-17 { color: #8E99B1; }
    .grvsc-mm-tEwury .grvsc-tEwury-8 { color: #56B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-3 { color: #6495EE; }
    .grvsc-mm-tEwury .grvsc-tEwury-6 { color: #79859D; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[Radix Themes vs Primitives]]></title><description><![CDATA[Explore the capabilities of Radix Themes, understand how it compares to other React component libraries, and learn its potential trade-offs.]]></description><link>https://freddydumont.com/blog/radix-ui-themes</link><guid isPermaLink="false">https://freddydumont.com/blog/radix-ui-themes</guid><pubDate>Mon, 14 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The recent release of &lt;a href=&quot;https://www.radix-ui.com/themes/&quot;&gt;Radix Themes&lt;/a&gt; piqued my interest but I feel some questions aren’t answered:&lt;/p&gt;
&lt;p&gt;How does this solution compare to other UI component libraries, and how difficult is it to customize? How does it differentiate itself in this crowded market of React component libraries?&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.radix-ui.com/primitives&quot;&gt;Radix Primitives&lt;/a&gt; is a library of accessible, &lt;strong&gt;unstyled&lt;/strong&gt; React components, initially &lt;a href=&quot;https://www.radix-ui.com/primitives/docs/overview/releases#december-15-2020&quot;&gt;released in 2020&lt;/a&gt;. They can be adopted independently, and styled using any approach: CSS classes, Tailwind, CSS-in-JS, etc.&lt;/p&gt;
&lt;p&gt;I recommend reading the &lt;a href=&quot;https://www.radix-ui.com/primitives/docs/overview/introduction&quot;&gt;Introduction page&lt;/a&gt; of their docs to understand what they’re about if you’re not familiar with headless component libraries.&lt;/p&gt;
&lt;p&gt;But to summarize quickly, these collections of components implement common UI patterns found on the web, that aren’t provided by the HTML spec, but are &lt;a href=&quot;https://www.w3.org/WAI/ARIA/apg/patterns/&quot;&gt;well documented&lt;/a&gt; and understood by the web development community.&lt;/p&gt;
&lt;p&gt;There’s a number of other headless component libraries in the React ecosystem.&lt;/p&gt;
&lt;p&gt;There’s &lt;a href=&quot;https://react-spectrum.adobe.com/react-aria/index.html&quot;&gt;React Aria&lt;/a&gt;, which focuses on accessibility with hooks and components for the patterns mentioned above, or &lt;a href=&quot;https://headlessui.com/&quot;&gt;Headless UI&lt;/a&gt;, which prioritizes compatibility with Tailwind.&lt;/p&gt;
&lt;p&gt;Other frameworks also have similar libraries.&lt;/p&gt;
&lt;h2&gt;How to use unstyled components&lt;/h2&gt;
&lt;p&gt;How does it work in practice? Let’s look at some code adapted from the &lt;a href=&quot;https://www.radix-ui.com/primitives/docs/overview/getting-started&quot;&gt;Radix Primitives docs&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;jsx&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; React &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; Popover &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;@radix-ui/react-popover&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;./styles.css&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;PopoverDemo&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;({ &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;triggerContent&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;popoverContent&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; })&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;Popover.Root&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;Popover.Trigger&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;PopoverTrigger&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;triggerContent&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;Popover.Trigger&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;Popover.Portal&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;      &amp;lt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;Popover.Content&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;PopoverContent&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;popoverContent&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;        &amp;lt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;Popover.Arrow&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;PopoverArrow&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;      &amp;lt;/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;Popover.Content&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;Popover.Portal&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;Popover.Root&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; PopoverDemo&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s quite straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Import the building blocks of the pattern you want to implement, which, in this case, are exported as a compound component.&lt;/li&gt;
&lt;li&gt;Create the structure using the component parts you imported.&lt;/li&gt;
&lt;li&gt;Add styles, either with &lt;code&gt;className&lt;/code&gt; as shown above, or with your preferred CSS solution.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;An unstyled component library such as Radix Primitives lets you abstract away the accessibility rabbit-hole and focus on the differentiating factor of your application, instead of reimplementing patterns that really should be part of the HTML spec.&lt;/p&gt;
&lt;p&gt;Overall, Primitives are an excellent choice for developers looking to implement their own design system.&lt;/p&gt;
&lt;h2&gt;Radix Themes&lt;/h2&gt;
&lt;p&gt;Moving on to Themes, an interesting release built on top of Radix Primitives.&lt;/p&gt;
&lt;p&gt;It’s a bit hard to define, and determine where it falls in the component library market.&lt;/p&gt;
&lt;p&gt;I’d like to say it’s a theming library for Primitives, but it appears be more limited than that. I’d want to compare it favorably to &lt;a href=&quot;https://theme-ui.com/&quot;&gt;Theme UI&lt;/a&gt;, but the latter is fully customizable, while Themes restrict you to the following tokens:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/radix-ui/themes/blob/main/packages/radix-ui-themes/src/styles/tokens/color.css&quot;&gt;Color&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/radix-ui/themes/blob/main/packages/radix-ui-themes/src/styles/tokens/radius.css&quot;&gt;Radius&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/radix-ui/themes/blob/main/packages/radix-ui-themes/src/styles/tokens/shadow.css&quot;&gt;Shadows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/radix-ui/themes/blob/main/packages/radix-ui-themes/src/styles/tokens/space.css&quot;&gt;Space&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/radix-ui/themes/blob/main/packages/radix-ui-themes/src/styles/tokens/typography.css&quot;&gt;Typography&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re expected to accept the look and feel of the components provided, in order to simplify development of simple pages and prototypes, I fear we’ll again see an endless stream of recognizable designs à la TailwindUI. A bunch of landing pages looking exactly the same, with slight differences in color schemes.&lt;/p&gt;
&lt;p&gt;What if you want to build a completely different UI, using the Themes components, would it make any sense or should you use Primitives directly?
Here’s what one of the co-founders of Radix UI had to say about the distinction between themes and primitives:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Totally, this is the idea. Primitives are great if you want full control to build your own design system from scratch. But a lot of folks are looking for something that helps them get started faster, with great design choices already baked in.&lt;/p&gt;— Stephen Haney (@sdothaney) &lt;a href=&quot;https://twitter.com/sdothaney/status/1689322197813481473&quot;&gt;August 9, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;And here’s what the announcement stated about styles:&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-dnt=&quot;true&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;Let’s talk about styles.&lt;br&gt;&lt;br&gt;1️⃣ Is Radix Themes a styling solution? – No.&lt;br&gt;2️⃣ Are you expected to write styles? – Sometimes, more if you feel adventurous.&lt;br&gt;3️⃣ I want to use Radix Themes with my favourite CSS tech. Will it work? – Yes!&lt;/p&gt;— Radix by WorkOS (@radix_ui) &lt;a href=&quot;https://twitter.com/radix_ui/status/1688912123563585538&quot;&gt;August 8, 2023&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;So it’s not a styling solution, but you should be able to modify the components to fit your requirements.&lt;/p&gt;
&lt;h2&gt;Radix Themes in practice&lt;/h2&gt;
&lt;p&gt;After playing around with Themes in a quickly scaffolded React app, here’s my conclusion: while styling is &lt;em&gt;possible&lt;/em&gt;, I wouldn’t say all components make it easy.&lt;/p&gt;
&lt;p&gt;For example, changing the border color of the &lt;code&gt;Card&lt;/code&gt; component requires either overwriting or disabling &lt;code&gt;box-shadow&lt;/code&gt; on an &lt;code&gt;::after&lt;/code&gt; pseudo-element using &lt;code&gt;!important&lt;/code&gt;, then setting a border on its child &lt;code&gt;div&lt;/code&gt; rather than the component itself.&lt;/p&gt;
&lt;p&gt;You’ll need to put in some effort and dig into the source code or devtools to understand where and how the built-in styles are applied.&lt;/p&gt;
&lt;p&gt;So it’s definitely not a styling solution for Radix Primitives, which I would have loved to see. Instead, it’s a themeable component library, limited in its customization options.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;For a custom design, better use Primitives directly, along with a token-based styling solution provided by another library.&lt;/p&gt;
&lt;p&gt;If, on the other hand, you’re happy with the look and feel of Radix Themes, and you’re trying to build something quickly, understanding that moving to your own design system later would require a rewrite to Primitives, Radix Themes might be a good pick.&lt;/p&gt;
&lt;p&gt;As with all pre-styled component libraries, you’re gaining speed now in exchange of a larger maintenance burden later, should you decide to give your project its own visual identity.&lt;/p&gt;
&lt;p&gt;The key is in understanding those trade-offs.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-16 { color: #7C4DFF; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-7 { color: #00BEC4; }
  .eva-light-bold .grvsc-tldFB4-11 { color: #C838C6; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-3 { color: #437AED; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-14 { color: #F0AA0B; }
  .eva-light-bold .grvsc-tldFB4-5 { color: #A0A1A7F2; }
  .eva-light-bold .grvsc-tldFB4-19 { color: #A0A1A7; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-16 { color: #A78CFA; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-8 { color: #56B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-12 { color: #CF68E1; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-3 { color: #6495EE; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-14 { color: #E4BF7F; }
    .grvsc-mm-tEwury .grvsc-tEwury-6 { color: #79859D; }
    .grvsc-mm-tEwury .grvsc-tEwury-19 { color: #8792AA; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[Separation of concerns]]></title><description><![CDATA[The principle of "Separation of concerns" is often misapplied to web technologies. HTML, CSS, and JS should work together in component-driven web development.]]></description><link>https://freddydumont.com/blog/separation-of-concerns</link><guid isPermaLink="false">https://freddydumont.com/blog/separation-of-concerns</guid><pubDate>Tue, 08 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Software development dogmas can take a long time to die.&lt;/p&gt;
&lt;p&gt;Take &lt;em&gt;Separation of concerns&lt;/em&gt;, an important principle of computer science, which, if not understood properly, leads to programs sectioned along inefficient, sometimes arbitrary lines.&lt;/p&gt;
&lt;p&gt;This confusion can be seen in a type of recurring comment, an unfunny and unintentional meme, that we see in online discourse. It goes something like that:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;HTML, CSS and JS being distinct languages, they are separate concerns.&lt;/li&gt;
&lt;li&gt;Modern component frameworks are bad, because they break this rule.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, let me preface this discussion by noting that it specifically applies to web &lt;em&gt;application&lt;/em&gt; development.&lt;/p&gt;
&lt;p&gt;If you’re not building a highly interactive application, in a component-driven way, your concerns might be different than the ones described here.&lt;/p&gt;
&lt;p&gt;With that out of the way, are the concerns of &lt;em&gt;Structure&lt;/em&gt;, &lt;em&gt;Style&lt;/em&gt;, and &lt;em&gt;Interactivity&lt;/em&gt; really separate?&lt;/p&gt;
&lt;h2&gt;Components in other languages&lt;/h2&gt;
&lt;p&gt;If one were to design a language from scratch today, whose purpose is to build interactive applications, would they divide it into three? One language to create the structure of the component, another to style the component and one more to define its behavior?&lt;/p&gt;
&lt;p&gt;I doubt anybody would think this is a good idea. Sounds pretty damn dumb if you ask me.&lt;/p&gt;
&lt;p&gt;Yet, we find people claiming that this is how web apps should be built, supporting their argument with the fact that HTML, CSS and JS are three different languages, thus three different “concerns”, as if oblivious to the historical context of how web technologies came to be.&lt;/p&gt;
&lt;p&gt;The web is HTML, CSS and JavaScript because it evolved from a read-only document sharing system, that later gained its styling and interactive capabilities, not because those technologies are the optimal &lt;abbr title=&quot;User Interface&quot;&gt;UI&lt;/abbr&gt; development paradigm.&lt;/p&gt;
&lt;p&gt;Let’s use a modern example by looking at SwiftUI, a somewhat recent approach to building user interfaces. Here’s some code pulled &lt;a href=&quot;https://developer.apple.com/documentation/swiftui/backyard-birds-sample#Construct-interactive-widgets&quot;&gt;from their docs&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;swift&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;*/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;Refill Water&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;, systemImage&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;arrow.clockwise&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    .&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;foregroundStyle&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;secondary&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    .&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;frame&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;maxWidth&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;infinity&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    .&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;vertical&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    .&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;horizontal&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    .&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;quaternary&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;, in&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt; .&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;containerRelative&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll notice that the notion of separating styling from the component structure is absent here.&lt;/p&gt;
&lt;p&gt;Now, I’m not pointing to SwiftUI as the definitive source for UI development. My own experience indicates that this approach is more efficient than separating structure and style.&lt;/p&gt;
&lt;p&gt;I’ve seen monolithic CSS files climb to thousands of lines, and methodologies such as &lt;abbr title=&quot;Blocks, Elements and Modifiers&quot;&gt;BEM&lt;/abbr&gt; to try and rein in the chaos, but nothing is as simple and elegant as component-scoped styles.&lt;/p&gt;
&lt;h2&gt;Presentation as a specific concern&lt;/h2&gt;
&lt;p&gt;Different frameworks handle styling differently, but most embrace the idea of component encapsulation.&lt;/p&gt;
&lt;p&gt;Having external styles impact your components in a cascade, originating from outside to dictate the appearance of child elements makes reasoning about, and debugging styles more challenging, requiring the developer to constantly keep extra context in mind.&lt;/p&gt;
&lt;p&gt;If you find yourself asking “Where is this CSS coming from?”, there’s an issue with your architecture. Your concerns are so well separated that you have no clue what’s going on.&lt;/p&gt;
&lt;p&gt;HTML, CSS and JS serve the same concern: presenting data. This is why frameworks like Vue and Svelte bundle them into &lt;a href=&quot;https://vuejs.org/guide/scaling-up/sfc.html&quot;&gt;single-file components&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While React doesn’t prescribe a styling approach, and uses JSX rather than &lt;abbr title=&quot;Single-file components&quot;&gt;SFCs&lt;/abbr&gt;, the popularity of CSS-in-JS and Tailwind is undeniable.&lt;/p&gt;
&lt;p&gt;Style encapsulation at the component level is more efficient and easier to work with, especially as the component library grows.&lt;/p&gt;
&lt;p&gt;Let’s take a look at an example, this time using &lt;a href=&quot;https://theme-ui.com/&quot;&gt;Theme UI&lt;/a&gt;, described as “a library for creating themeable user interfaces based on constraint-based design principles.”&lt;/p&gt;
&lt;p&gt;To keep things simple, I’m pulling the example from &lt;a href=&quot;https://theme-ui.com/#create-your-theme&quot;&gt;their front page&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;jsx&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Create your theme&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; type &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; Theme &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;theme-ui&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;Theme&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;fonts&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;system-ui, sans-serif&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;heading&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;&amp;quot;Avenir Next&amp;quot;, sans-serif&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;monospace&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;Menlo, monospace&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;#000&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;#fff&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;primary&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;#33e&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; Style your UI&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;/**&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tEwury-16&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;jsxImportSource&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; theme-ui &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;*/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; ThemeUIProvider &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;theme-ui&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; theme &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;./theme&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;export&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;ThemeUIProvider&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;h1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;sx&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;primary&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;fontFamily&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;heading&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;    &amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;      Hello&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;    &amp;lt;/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;h1&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;ThemeUIProvider&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how style information is defined as data in the theme, which is then used in the JSX template via the &lt;code&gt;sx&lt;/code&gt; prop.&lt;/p&gt;
&lt;p&gt;Again, I’m not advocating for a specific library here; I’m using this example to illustrate that web technologies &lt;em&gt;collectively&lt;/em&gt; address the same &lt;em&gt;presentation&lt;/em&gt; concern, which includes both structure &lt;strong&gt;and&lt;/strong&gt; style.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In the era of component-based UI frameworks, structure, style, and interactivity are not individual “concerns” that should be separated. They collectively serve the same fundamental &lt;em&gt;presentation&lt;/em&gt; concern.&lt;/p&gt;
&lt;p&gt;Web technologies evolved into what they are today to bring new capabilities to the platform. We shouldn’t limit ourselves to a dogmatic separation of those technologies that idolizes a perceived purity over practical efficiency.&lt;/p&gt;
&lt;p&gt;Good abstractions should be discovered, rather than created. As you work in a codebase, its different concerns become readily apparent. In a web application, they may be things like &lt;em&gt;data fetching&lt;/em&gt;, &lt;em&gt;state management&lt;/em&gt;, and &lt;em&gt;business logic&lt;/em&gt;, not the artificial boundaries between HTML, CSS, and JS.&lt;/p&gt;
&lt;p&gt;So, the next time you hear someone mention “separation of concerns” as if the phrase itself were an argument, feel free to challenge them and ask what concerns they’re trying to separate. Chances are they haven’t put much thought into it.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-i { font-style: italic; }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-3 { color: #437AED; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-5 { color: #A0A1A7F2; }
  .eva-light-bold .grvsc-tldFB4-6 { color: #FF6D12; }
  .eva-light-bold .grvsc-tldFB4-16 { color: #7C4DFF; }
  .eva-light-bold .grvsc-tldFB4-11 { color: #C838C6; }
  .eva-light-bold .grvsc-tldFB4-17 { color: #8E8E90; }
  .eva-light-bold .grvsc-tldFB4-7 { color: #00BEC4; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-19 { color: #A0A1A7; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-i { font-style: italic; }
    .grvsc-mm-tEwury .grvsc-tEwury-3 { color: #6495EE; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-6 { color: #79859D; }
    .grvsc-mm-tEwury .grvsc-tEwury-7 { color: #FF9070; }
    .grvsc-mm-tEwury .grvsc-tEwury-16 { color: #A78CFA; }
    .grvsc-mm-tEwury .grvsc-tEwury-12 { color: #CF68E1; }
    .grvsc-mm-tEwury .grvsc-tEwury-17 { color: #8E99B1; }
    .grvsc-mm-tEwury .grvsc-tEwury-8 { color: #56B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-19 { color: #8792AA; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[How to prevent Theme UI color mode flash]]></title><description><![CDATA[Theme UI's `system` color mode can color flash when responding to the `prefers-color-scheme` media query. Here's a hack to prevent that.]]></description><link>https://freddydumont.com/blog/theme-ui-color-flash</link><guid isPermaLink="false">https://freddydumont.com/blog/theme-ui-color-flash</guid><pubDate>Sat, 18 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Back when Theme UI was first introduced, the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query didn’t exist. You’d have a button on the website to switch between light and dark mode, or any other color scheme provided by the site’s developers.&lt;/p&gt;
&lt;p&gt;This worked well, until the standardization of the color scheme media query. Nowadays, most websites implement color schemes by responding to the user’s preference via CSS.&lt;/p&gt;
&lt;p&gt;To work with this new expectation, Theme UI introduced the &lt;code&gt;system&lt;/code&gt; color mode, which would respond to the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media query.&lt;/p&gt;
&lt;p&gt;However, this functionality is &lt;a href=&quot;https://github.com/system-ui/theme-ui/blob/8aa5c36fa890c7a98673b2b9706d3b303def37e7/packages/color-modes/src/index.tsx&quot;&gt;implemented in JavaScript&lt;/a&gt;, not in CSS, so there can be a color mode flash on initial page load, because the document is rendered before the JS bundle is evaluated.&lt;/p&gt;
&lt;p&gt;Reading through the source code linked above, you can’t help but wonder why this system wasn’t scrapped entirely. It feels incredibly overengineered by today’s standards, where a CSS media query along with an event listener would do the trick.&lt;/p&gt;
&lt;p&gt;It’d be great if we could opt out of this behavior, but alas, we can’t. So here’s a hack that will prevent the color flash to occur:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;js&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;(prefers-color-scheme: dark)&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  document&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;documentElement&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;theme-ui-dark&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  document&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;documentElement&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;theme-ui-__default&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;window&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;(prefers-color-scheme: dark)&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;change&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;dark mode&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      document&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;documentElement&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;theme-ui-__default&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      document&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;documentElement&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;theme-ui-dark&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;light mode&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      document&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;documentElement&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;theme-ui-dark&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      document&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;documentElement&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;classList&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;theme-ui-__default&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that for this hack to work, it needs to block rendering, so you have to add it in a &lt;code&gt;&amp;#x3C;script&gt;&lt;/code&gt; before the closing &lt;code&gt;&amp;#x3C;/head&gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;Methods will vary depending on the framework; I added it as a child of &lt;code&gt;&amp;#x3C;Helmet&gt;&lt;/code&gt; for my site. As far as I can tell, there is no perceptible performance impact.&lt;/p&gt;
&lt;p&gt;You might wonder how this hack even works. The reason is Theme UI ships a set of CSS custom properties for each color mode with the initial document request.&lt;/p&gt;
&lt;p&gt;The CSS looks like this:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;css&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-20 grvsc-tEwury-20&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;--theme-ui-colors-text&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;#1c1917&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;--theme-ui-colors-background&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;#fafafa&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-20 grvsc-tEwury-20&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;.theme-ui-__default&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;.theme-ui-__default&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt; html&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-20 grvsc-tEwury-20&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;--theme-ui-colors-text&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;#1c1917&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;--theme-ui-colors-background&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;#fafafa&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-20 grvsc-tEwury-20&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;.theme-ui-dark&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;.theme-ui-dark&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt; html&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-20 grvsc-tEwury-20&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;--theme-ui-colors-text&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;#f5f5f5&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;--theme-ui-colors-background&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;#171717&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-20 grvsc-tEwury-20&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you open the devtools and switch between color schemes, you’ll notice that Theme UI replaces the values of the CSS custom properties set on the &lt;code&gt;html&lt;/code&gt; tag, the first block in the above example.&lt;/p&gt;
&lt;p&gt;Our script overrides Theme UI’s handling of color modes entirely, by instead switching the class on the &lt;code&gt;html&lt;/code&gt; tag between &lt;code&gt;theme-ui-dark&lt;/code&gt; and &lt;code&gt;theme-ui-__default&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity&quot;&gt;higher specificity&lt;/a&gt; of classes mean that whatever is set on &lt;code&gt;html&lt;/code&gt; is irrelevant.&lt;/p&gt;
&lt;p&gt;Be aware that Theme UI’s behavior could change in the future, make sure you read the release notes before upgrading major versions.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-i { font-style: italic; }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-11 { color: #C838C6; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-3 { color: #437AED; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-5 { color: #A0A1A7F2; }
  .eva-light-bold .grvsc-tldFB4-14 { color: #F0AA0B; }
  .eva-light-bold .grvsc-tldFB4-20 { color: #888888D9; }
  .eva-light-bold .grvsc-tldFB4-17 { color: #8E8E90; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-i { font-style: italic; }
    .grvsc-mm-tEwury .grvsc-tEwury-b { font-weight: bold; }
    .grvsc-mm-tEwury .grvsc-tEwury-12 { color: #CF68E1; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-3 { color: #6495EE; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-6 { color: #79859D; }
    .grvsc-mm-tEwury .grvsc-tEwury-14 { color: #E4BF7F; }
    .grvsc-mm-tEwury .grvsc-tEwury-20 { color: #838FA7CC; }
    .grvsc-mm-tEwury .grvsc-tEwury-17 { color: #8E99B1; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[How to use PostGIS with Prisma]]></title><description><![CDATA[Prisma doesn’t support PostGIS, but we can query spatial data using raw database access. This post explores PostGIS basics and the Prisma setup.]]></description><link>https://freddydumont.com/blog/prisma-postgis</link><guid isPermaLink="false">https://freddydumont.com/blog/prisma-postgis</guid><pubDate>Sun, 03 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At the time of writing, &lt;a href=&quot;https://github.com/prisma/prisma/issues/2789&quot;&gt;Prisma doesn’t support PostGIS&lt;/a&gt;. However, we can still make spatial queries using &lt;a href=&quot;https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access&quot;&gt;raw database access&lt;/a&gt;. The setup is fairly simple, but it’s not documented so this posts goes into that.&lt;/p&gt;
&lt;h2&gt;Requirements&lt;/h2&gt;
&lt;p&gt;You’ll need to use raw queries, so &lt;strong&gt;some SQL knowledge&lt;/strong&gt; is required.&lt;/p&gt;
&lt;p&gt;To actually work with spatial data, you’ll also need to understand &lt;strong&gt;how PostGIS works&lt;/strong&gt; and what functions are available.&lt;/p&gt;
&lt;p&gt;There’s surprisingly little information for PostGIS beginners online and the documentation is quite difficult to navigate unless you know exactly what you’re looking for, so I’ll go over the basics in the next section.&lt;/p&gt;
&lt;p&gt;If you want a complete course, I used the &lt;a href=&quot;https://learnsql.com/course/postgis&quot;&gt;LearnSQL.com PostGIS course&lt;/a&gt; and can easily recommend it.&lt;/p&gt;
&lt;p&gt;Feel free to skip to &lt;a href=&quot;#installing-postgis&quot;&gt;Installing PostGIS&lt;/a&gt; if you just want the Prisma setup.&lt;/p&gt;
&lt;h2&gt;PostGIS basics&lt;/h2&gt;
&lt;p&gt;There are two types of spatial data in PostGIS: geometry and geography:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Geometry&lt;/strong&gt; is for 2D calculations: faster, but inaccurate at global scale.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Geography&lt;/strong&gt; is for 3D calculations: exact, but slower.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s because geometry calculates on a map projection (a plane) while geography takes the actual shape of the earth (a globe) into account. In most cases, you’ll want to use geometry, and this is what this post assumes.&lt;/p&gt;
&lt;p&gt;In PostGIS, &lt;code&gt;X&lt;/code&gt; refers to the longitude (vertical lines) and &lt;code&gt;Y&lt;/code&gt; refers to the latitude (horizontal lines).&lt;/p&gt;
&lt;p&gt;There are 3 basic subtypes of geometries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POINT(X Y)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LINESTRING(X Y, X Y, ...)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POLYGON((X Y, X Y, ...), (X Y, X Y, ...), ...)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, you can interact with spatial data through PostGIS functions. Here are a few examples:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://postgis.net/docs/ST_MakePoint.html&quot;&gt;ST_MakePoint&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ST_MakePoint(-71.1043443253471, 42.3150676015829);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a &lt;code&gt;POINT&lt;/code&gt; from coordinates that can be inserted into a geometry column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://postgis.net/docs/ST_Distance.html&quot;&gt;ST_Distance&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ST_Distance(A, B);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns the smallest distance between two geometries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://postgis.net/docs/ST_DWithin.html&quot;&gt;ST_DWithin&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ST_DWithin(A, B, distance);&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns true if &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt; are within &lt;code&gt;distance&lt;/code&gt; of each other&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;What SRID should I use?&lt;/h2&gt;
&lt;p&gt;The SRID is a unique identifier for a specific coordinate system. There are worldwide and local systems as well as systems in degrees or meters.&lt;/p&gt;
&lt;p&gt;From what I understand, &lt;a href=&quot;https://epsg.io/4326&quot;&gt;EPSG:4326&lt;/a&gt; appears to be the standard global coordinate system using degrees (latitude and longitude) and &lt;a href=&quot;https://epsg.io/3857&quot;&gt;EPSG:3857&lt;/a&gt; is the system used by web mapping applications such as Google Maps.&lt;/p&gt;
&lt;p&gt;For my project, I opted to store coordinates as 4326. If I need a distance in meters, I’ll convert to 3857 using &lt;a href=&quot;https://postgis.net/docs/ST_Transform.html&quot;&gt;&lt;code&gt;ST_Transform&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For more info, see &lt;a href=&quot;https://gis.stackexchange.com/q/48949&quot;&gt;this StackExchange answer&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;installing-postgis&quot;&gt;Installing PostGIS&lt;/h2&gt;
&lt;p&gt;First, you need to make sure that your database supports PostGIS.&lt;/p&gt;
&lt;p&gt;In most cases, managed cloud PostgreSQL databases enable it out of the box. If you’re working locally, I suggest you take a look at the &lt;a href=&quot;https://postgis.net/install/&quot;&gt;installation instructions&lt;/a&gt; for your platform.&lt;/p&gt;
&lt;p&gt;I opted for &lt;a href=&quot;https://postgresapp.com/&quot;&gt;Postgres.app&lt;/a&gt; which conveniently includes PostGIS. Highly recommend it if you’re on macOS and want a simple setup.&lt;/p&gt;
&lt;h2&gt;Adding PostGIS support to Prisma&lt;/h2&gt;
&lt;p&gt;Since Prisma 4.0, &lt;code&gt;extendedIndexes&lt;/code&gt; is Generally Available. If you’re not on version 4 yet, you’ll need to enable some preview features.&lt;/p&gt;
&lt;p&gt;Here’s the schema we’ll be using as an example:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;prisma&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;datasource &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  provider &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;postgresql&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  url      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;DATABASE_URL&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;generator &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  provider        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;prisma-client-js&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  binaryTargets   &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;native&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;// preview features to enable if not on v4:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  previewFeatures &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; [&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;extendedIndexes&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;improvedQueryRaw&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;// we&amp;#39;ll assume we had this simple model before enabling postgis&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;model &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  id        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;@id&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  createdAt &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;@default&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;())&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  updatedAt &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;@updatedAt&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, to be able to run PostGIS functions on the &lt;code&gt;Location&lt;/code&gt; table, we’ll add the following lines:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;prisma&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;model &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  id        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;@id&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  createdAt &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;@default&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;())&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  updatedAt &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;DateTime&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;@updatedAt&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;// the geometry column itself&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  coords    Unsupported(&amp;quot;geometry(Point, 4326)&amp;quot;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;// the index for that column&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;@@index&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;coords&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;], &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;location_idx&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;, &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;Gist&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s break this down:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Unsupported&lt;/code&gt; field type tells Prisma Migrate to use a specified type for a column. This data &lt;strong&gt;will not be available&lt;/strong&gt; to the Prisma Client. &lt;a href=&quot;https://www.prisma.io/docs/concepts/components/prisma-schema/features-without-psl-equivalent#unsupported-database-features&quot;&gt;More info in the Prisma docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Unsupported&lt;/code&gt; doesn’t work on its own, so we give it a type. It could be as simple as &lt;code&gt;&quot;geometry&quot;&lt;/code&gt;, but here we also specify one of the subtypes and SRIDs seen above.&lt;/li&gt;
&lt;li&gt;The index is still confusing to me. &lt;a href=&quot;https://github.com/prisma/prisma/issues/1798#issuecomment-1147728900&quot;&gt;I’m told you need to use it&lt;/a&gt;. I understand the reasoning behind its usage, but I couldn’t describe exactly what it does. Database stuff ¯\_(ツ)_/¯.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Prisma versions 4.5 and above &lt;a href=&quot;https://www.prisma.io/docs/concepts/components/prisma-schema/postgresql-extensions&quot;&gt;support PostgreSQL extensions&lt;/a&gt; through a preview feature. There shouldn’t be any problem during migration if you enable it, so the following section will not be relevant.&lt;/p&gt;
&lt;p&gt;If we attempt to migrate at this stage, we’ll most likely encounter this error:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;sh&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;Error: db error: ERROR: &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;geometry&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; does not exist&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;   0: sql_migration_connector::apply_migration::apply_migration&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;             at migration-engine/connectors/sql-migration-connector/src/apply_migration.rs:9&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;   1: migration_core::state::SchemaPush&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;             at migration-engine/core/src/state.rs:349&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s because the PostGIS extension needs to be created in the database before we can use its types and functions. To do that, run the following command:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;sh&quot; data-index=&quot;3&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;prisma migrate dev --create-only&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will &lt;a href=&quot;https://www.prisma.io/docs/reference/api-reference/command-reference#prisma-migrate&quot;&gt;create a SQL migration without applying it&lt;/a&gt;, thus allowing you to modify it. The migration file will look like this:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;sql&quot; data-index=&quot;4&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; AlterTable&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;ALTER&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;TABLE&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;Location&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;ADD COLUMN &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;coords&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;geometry&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;(Point, &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;4326&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; CreateIndex&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; INDEX &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;location_idx&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;ON&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;Location&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;USING&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; GIST (&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;coords&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the following statement on top:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;sql&quot; data-index=&quot;5&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; create postgis extension&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;CREATE&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; EXTENSION postgis;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; AlterTable&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-13 grvsc-tEwury-4&quot;&gt;[...]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now apply the migration:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;bash&quot; data-index=&quot;6&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;prisma migrate dev&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it! Your can now send PostGIS-enabled &lt;a href=&quot;https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access#raw-queries-with-relational-databases&quot;&gt;raw queries&lt;/a&gt; to your database. The development experience is far from what we’re used to with Prisma, but while we wait for native support, it’s better than nothing.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-i { font-style: italic; }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-7 { color: #00BEC4; }
  .eva-light-bold .grvsc-tldFB4-19 { color: #A0A1A7; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-3 { color: #437AED; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-15 { color: #EF8ED8; }
  .eva-light-bold .grvsc-tldFB4-6 { color: #FF6D12; }
  .eva-light-bold .grvsc-tldFB4-14 { color: #F0AA0B; }
  .eva-light-bold .grvsc-tldFB4-16 { color: #7C4DFF; }
  .eva-light-bold .grvsc-tldFB4-13 { color: #626264; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-i { font-style: italic; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-8 { color: #56B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-19 { color: #8792AA; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-3 { color: #6495EE; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-15 { color: #FF6AB3; }
    .grvsc-mm-tEwury .grvsc-tEwury-7 { color: #FF9070; }
    .grvsc-mm-tEwury .grvsc-tEwury-14 { color: #E4BF7F; }
    .grvsc-mm-tEwury .grvsc-tEwury-16 { color: #A78CFA; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[Why I chose Gatsby (again) for my developer blog]]></title><description><![CDATA[A reflection on analysis paralysis and the value of familiarity in a world of fast changing technology.]]></description><link>https://freddydumont.com/blog/why-gatsby</link><guid isPermaLink="false">https://freddydumont.com/blog/why-gatsby</guid><pubDate>Thu, 19 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Working on a side project, I encountered some technical problems that would make for interesting blog posts. I wrote them, but didn’t want to publish on my website as it looked and felt outdated to me.&lt;/p&gt;
&lt;p&gt;I built it in 2019, and it didn’t change much since then. A makeover was long overdue.&lt;/p&gt;
&lt;h2&gt;The framework rabbit hole&lt;/h2&gt;
&lt;p&gt;The previous iteration of my website used Gatsby, but the blog itself relied on an external theme that wasn’t maintained anymore. This time, I wanted control over my blog platform.&lt;/p&gt;
&lt;p&gt;In an attempt to ship without delaying my side project, I spun up a repo based on my &lt;a href=&quot;https://github.com/freddydumont/twin-macro-next-boilerplate&quot;&gt;twin-macro-next-boilerplate&lt;/a&gt; template. I knew that comparing all different frameworks would be a waste of time, so I went with what I use most frequently.&lt;/p&gt;
&lt;p&gt;But something felt off.&lt;/p&gt;
&lt;p&gt;I like building web apps with Next.js due to its flexibility, but for a static blog, it’s not ideal.&lt;/p&gt;
&lt;p&gt;Filesystem routing with Markdown files isn’t that great if you want a different structure in the repo than on the blog. The alternative would be to use the Next.js data fetching functions to parse Markdown manually and generate slugs, which isn’t a big deal, but again, not ideal.&lt;/p&gt;
&lt;p&gt;What about using &lt;a href=&quot;https://astro.build/&quot;&gt;astro&lt;/a&gt; instead, a fairly new static site generator (SSG) which promises “peak performance”?&lt;/p&gt;
&lt;p&gt;I seriously considered it until I generated the blog starter. I was faced with the prospect of learning yet another framework, and either starting from scratch or customizing a bloated starter. And to be fair to other SSGs, there’s no perceptible difference in load times unless you’re on a slow 3G connection.&lt;/p&gt;
&lt;p&gt;Not what I was looking for, so I briefly explored using &lt;a href=&quot;https://remix.run/&quot;&gt;Remix&lt;/a&gt;, but felt it was overkill for a simple static site. All I really needed was a Markdown-based blog so I could get back to my side project.&lt;/p&gt;
&lt;p&gt;You see where this is going. I fell into the trap I was trying to avoid: &lt;em&gt;analysis paralysis&lt;/em&gt;. When all options seem equally good, with different tradeoffs, it can be hard to make a decision.&lt;/p&gt;
&lt;p&gt;So what’s the solution?&lt;/p&gt;
&lt;h2&gt;A familiar ally&lt;/h2&gt;
&lt;p&gt;A thought remained in the back of my mind as I was exploring all these frameworks: just use Gatsby again. I like its GraphQL data loader, its performance is great, it handles the developer blog scenario well, and most of all: I know how it works.&lt;/p&gt;
&lt;p&gt;Here’s a quick rundown of its benefits in case you’re in the same paralysed boat:&lt;/p&gt;
&lt;h3&gt;Why Gatsby&lt;/h3&gt;
&lt;p&gt;At first glance, Gatsby can look more limited that other options because of its approach to data loading.&lt;/p&gt;
&lt;p&gt;While other frameworks such as Next.js provide a completely flexible loader, Gatsby hands you a large set of lifecycle hooks that allow you to customize its GraphQL-based data loader.&lt;/p&gt;
&lt;p&gt;This “limited” architecture is exactly its strength. It enables the community to share plugins, and developers to inspect all available data at any time through the GraphiQL explorer.&lt;/p&gt;
&lt;p&gt;Out of the box, Gatsby does a lot for you. The blog starter is easy to understand and customize, and the plugin ecosystem makes adding common and non-trivial functionality easy.&lt;/p&gt;
&lt;p&gt;For example, here’s all the code this blog needs for build-time syntax highlighting compatible with VSCode themes:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;ts&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; under `gatsby-transformer-remark` plugins:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`gatsby-remark-vscode`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;Eva Light Bold&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;dark&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;Eva Dark&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;extensions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;Eva-Theme/VSCode&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;vscode-graphql&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-15 grvsc-tEwury-15&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’d say Gatsby’s good enough.&lt;/p&gt;
&lt;p&gt;There’s value in doing R&amp;#x26;D and exploring all different options, but sooner or later a decision has to be made. If the end result would be similar with all options, familiarity with a technology should be taken into account.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-i { font-style: italic; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-15 { color: #EF8ED8; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-17 { color: #8E8E90; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-5 { color: #A0A1A7F2; }
  .eva-light-bold .grvsc-tldFB4-11 { color: #C838C6; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-i { font-style: italic; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-15 { color: #FF6AB3; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-17 { color: #8E99B1; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-6 { color: #79859D; }
    .grvsc-mm-tEwury .grvsc-tEwury-12 { color: #CF68E1; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[How to throw an error from an invoked child machine to a parent in xstate]]></title><description><![CDATA[Errors thrown inside invoked promises will trigger the `onError` block, but invoked child machines require usage of the `escalate` action.]]></description><link>https://freddydumont.com/blog/xstate-escalate-error</link><guid isPermaLink="false">https://freddydumont.com/blog/xstate-escalate-error</guid><pubDate>Sun, 24 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The basic syntax for invoking a child machine is the same as for invoking promises or any other service:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;tsx&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;current_state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; childMachine&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; optional initial context&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;onDone&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;next_state&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;someAction&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;onError&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;error&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;printError&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you invoke a promise, an error thrown inside will automatically trigger the &lt;code&gt;onError&lt;/code&gt; block. You could expect the same thing to happen when invoking a child machine and throw an error like so:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;tsx&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;states&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;fetchSomething&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;onDone&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;upsert&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;someAction&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;onError&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt; event&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; event&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;data&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;upsert&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;success&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;final&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;However, this doesn’t work, and the &lt;a href=&quot;https://xstate.js.org/docs/guides/communication.html#invoking-machines&quot;&gt;“Invoking services” docs&lt;/a&gt; only mention &lt;code&gt;sendParent&lt;/code&gt; as a way to communicate from child to parent machine.&lt;/p&gt;
&lt;p&gt;You could use &lt;code&gt;sendParent&lt;/code&gt; to solve the issue, but you’d need to send a custom event from your child machine and handle it in the parent, outside of the &lt;code&gt;onError&lt;/code&gt; block:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;tsx&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ... child machine&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;throwError&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;sendParent&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt; event&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;({&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;CHILD_ERROR&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; event&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}))&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ... parent machine&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;current_state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; childMachine&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ... &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;*/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;onDone&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;/*&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ... &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;*/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;CHILD_ERROR&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;error&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;printError&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s actually what I did until a friendly discord user pointed me in the right direction.&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;Turns out the answer was in the “Actions” page of the docs, under &lt;a href=&quot;https://xstate.js.org/docs/guides/actions.html#escalate-action&quot;&gt;the &lt;code&gt;escalate&lt;/code&gt; action&lt;/a&gt;, which “&lt;em&gt;escalates an error by sending it to the parent machine.&lt;/em&gt;” Exactly what we need.&lt;/p&gt;
&lt;p&gt;Here’s how you’d refactor the &lt;code&gt;throwError&lt;/code&gt; action so that it would trigger the &lt;code&gt;onError&lt;/code&gt; block of the parent:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;tsx&quot; data-index=&quot;3&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;throwError&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;escalate&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt; event&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;({&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; event&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}))&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-i { font-style: italic; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-17 { color: #8E8E90; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-5 { color: #A0A1A7F2; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-14 { color: #F0AA0B; }
  .eva-light-bold .grvsc-tldFB4-3 { color: #437AED; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-b { font-weight: bold; }
    .grvsc-mm-tEwury .grvsc-tEwury-i { font-style: italic; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-17 { color: #8E99B1; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-6 { color: #79859D; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-14 { color: #E4BF7F; }
    .grvsc-mm-tEwury .grvsc-tEwury-3 { color: #6495EE; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[How to test xstate machines invoking async code in Jest]]></title><description><![CDATA[Prevent xstate machines async invocations from persisting after the test is done by using `waitFor` and keeping a reference to the actor.]]></description><link>https://freddydumont.com/blog/xstate-async-testing</link><guid isPermaLink="false">https://freddydumont.com/blog/xstate-async-testing</guid><pubDate>Fri, 22 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m writing a complex state machine which invokes a number of promises. Those promises are calls to an external API or to the database.&lt;/p&gt;
&lt;p&gt;The tests use &lt;a href=&quot;https://mswjs.io/&quot;&gt;&lt;code&gt;msw&lt;/code&gt;&lt;/a&gt; to mock API calls, and a test database which is seeded and torn down before and after each test.&lt;/p&gt;
&lt;p&gt;Since my state machine moves from one state to another sequentially, I opted to test that my implementation was correct at each step. Here’s an example, adapted from &lt;a href=&quot;https://xstate.js.org/docs/guides/communication.html#testing&quot;&gt;the &lt;code&gt;xstate&lt;/code&gt; docs&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;tsx&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;call_locations_api&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;should save locations to context on success and go to compare step&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;interpret&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;updateLocationsMachine&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;onTransition&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;compare&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;locations&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;toStrictEqual&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;locations&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I actually used an async/await variation, but for the sake of this article I’m using the &lt;code&gt;done()&lt;/code&gt; callback as presented in the docs. The result is the same.&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;While tests fail or succeed as expected, the machine continues until it reaches a final state, logging a bunch of database errors to the console or worst, making API calls after the &lt;code&gt;msw&lt;/code&gt; listener closes.&lt;/p&gt;
&lt;p&gt;Calling &lt;code&gt;done()&lt;/code&gt; only ends the test, not the machine itself.&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;Hidden under the &lt;a href=&quot;https://xstate.js.org/docs/guides/interpretation.html#waitfor&quot;&gt;“Interpreting machines” page&lt;/a&gt; of the &lt;code&gt;xstate&lt;/code&gt; docs is a section about &lt;code&gt;waitFor&lt;/code&gt;, which contains the following snippet:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;tsx&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;myFunc&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; async &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;actor&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;interpret&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;machine&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;doneState&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;actor&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;done&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;doneState&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; &amp;#39;done&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Huh, this is quite similar to the test code. Two key differences:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We keep a reference to the interpreted machine (&lt;code&gt;actor&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The asynchronous nature of the code is explicit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Adapting that to a test function, we can refactor our code:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;tsx&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; waitFor &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;xstate/lib/waitFor&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;call_locations_api&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;should save locations to context on success and go to compare step&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;async&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;actor&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;interpret&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;updateLocationsMachine&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;waitFor&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;actor&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;matches&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;compare&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;locations&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;toStrictEqual&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;locations&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    actor&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keeping a reference to the machine (&lt;code&gt;actor&lt;/code&gt;) allows us to stop it at the end of the test.&lt;/p&gt;
&lt;p&gt;This solves the problem by preventing the state machine from persisting and, as a bonus, it makes the code more readable by extracting the assertion from the &lt;code&gt;interpret&lt;/code&gt; block.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-i { font-style: italic; }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-3 { color: #437AED; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-5 { color: #A0A1A7F2; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-14 { color: #F0AA0B; }
  .eva-light-bold .grvsc-tldFB4-11 { color: #C838C6; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-16 { color: #7C4DFF; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-i { font-style: italic; }
    .grvsc-mm-tEwury .grvsc-tEwury-b { font-weight: bold; }
    .grvsc-mm-tEwury .grvsc-tEwury-3 { color: #6495EE; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-6 { color: #79859D; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-14 { color: #E4BF7F; }
    .grvsc-mm-tEwury .grvsc-tEwury-12 { color: #CF68E1; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-16 { color: #A78CFA; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[How to whitelist dynamic Tailwind classes in PurgeCSS]]></title><description><![CDATA[Avoid copy pasting classes with a quick JavaScript function and use the PurgeCSS whitelist configuration option.]]></description><link>https://freddydumont.com/blog/dynamic-tailwind-purgecss</link><guid isPermaLink="false">https://freddydumont.com/blog/dynamic-tailwind-purgecss</guid><pubDate>Fri, 13 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I was building an array of colorful tags to use in my &lt;code&gt;PortfolioCard&lt;/code&gt; components.&lt;/p&gt;
&lt;p&gt;Since many cards could share the same tags, I didn’t want to declare colors in my data. It would have led to repetition that would be problematic if I ever needed to change them.&lt;/p&gt;
&lt;p&gt;I opted instead to use Gatsby’s Node APIs to transform my array of tag names into an array of objects at build time.&lt;/p&gt;
&lt;p&gt;I’d maintain an object whose keys correspond to the tag names in my data, and whose values are an array, where the first item is a background color and the second is a text color:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;javascript&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;TAG_COLORS&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;javascript&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;bg-yellow-vivid-400&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;text-gray-900&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;react&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;bg-blue-400&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;text-gray-900&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;gatsby&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;bg-purple-700&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;text-purple-100&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So this JSON data:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;json&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;tags&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;react&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;gatsby&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;jest&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is converted into this JavaScript array to be consumed by my &lt;code&gt;Tags&lt;/code&gt; component:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;javascript&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;javascript&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;bg-yellow-vivid-400&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;text-gray-900&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;react&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;bg-blue-400&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;text-gray-900&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;gatsby&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;bg-purple-700&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;text-purple-100&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;jest&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;bg-red-vivid-800&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;text-white&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s what the rendered component looks like:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 658px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a40df15c2e1283189e8fdd60d670bc27/889a4/tags.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 16.666666666666664%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAABYlAAAWJQFJUiTwAAAA6klEQVR42g3MwS7DcADA4b4W/tV2Om3XVTh11k5iG9F0mSXWdpKaZIxZdiEEbzA3xBk3iYuLpDsQkeA1fnb4rp+Uzwnamwq7gUq8pdIJNMKKQDMdjPUIsxajr26gyjbGbB1TrmKIOtqcg1Mr47YbUyErQRUhBFIyjf4edd5uVCZ3Od5vF/i8nyEYXVIa/+Jev+KNM0pL51y0vrhKPzhr/uAvnpJmD0TZM/HkicPvF/SihbRsy4w6Kkc7CoNIZZho9FsCy61Q2B5SaPSxwn3yWpk1p4dX7OLbBxjzHu5eE/+ki3ec4vUSZEXhH9ebiRH6SgRfAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;list of tags&quot;
        title=&quot;&quot;
        src=&quot;/static/a40df15c2e1283189e8fdd60d670bc27/889a4/tags.png&quot;
        srcset=&quot;/static/a40df15c2e1283189e8fdd60d670bc27/8514f/tags.png 192w,
/static/a40df15c2e1283189e8fdd60d670bc27/804b2/tags.png 384w,
/static/a40df15c2e1283189e8fdd60d670bc27/889a4/tags.png 658w&quot;
        sizes=&quot;(max-width: 658px) 100vw, 658px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;Since my CSS classes are injected into the component at build time, PurgeCSS isn’t aware of them.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;jsx&quot; data-index=&quot;3&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;Tag&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;({ &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; })&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;className&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tEwury-3&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tEwury-3&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tEwury-3&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tEwury-3&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt; inline-block rounded-lg ...`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;  &amp;lt;/&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-19 grvsc-tEwury-19&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See how these two color classes are variables as opposed to others classes who are static?&lt;/p&gt;
&lt;p&gt;That’s why I needed to use the &lt;a href=&quot;https://www.purgecss.com/configuration#options&quot;&gt;whitelist configuration option&lt;/a&gt;. But there’s no way I would be copy pasting classes around and manually managing a whitelist.&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;We need to get all the &lt;code&gt;TAG_COLORS&lt;/code&gt; values and convert them to an array of strings that the PurgeCSS whitelist option will accept.&lt;/p&gt;
&lt;p&gt;A simple function does the trick:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;javascript&quot; data-index=&quot;4&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;whitelistedClasses&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;TAG_COLORS&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;whitelistedColors&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;currentColors&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;whitelistedColors&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;currentColors&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Object.values(TAG_COLORS)&lt;/code&gt; does what its name implies and returns an array of values.&lt;/p&gt;
&lt;p&gt;Those values are arrays themselves, so we need to &lt;code&gt;reduce&lt;/code&gt; everything to a single array. No need to worry about duplicates, PurgeCSS is smart enough to either filter or ignore them.&lt;/p&gt;
&lt;p&gt;All we need to do now is import &lt;code&gt;whitelistedClasses&lt;/code&gt; into our config and the problem is solved!&lt;/p&gt;
&lt;p&gt;I’m using the Gatsby plugin so here’s how to do it in &lt;code&gt;gatsby-config.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;javascript&quot; data-index=&quot;5&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;whitelistedClasses&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;./src/utils/tags&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`gatsby-plugin-purgecss`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;tailwind&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-11 grvsc-tldFB4-b grvsc-tEwury-12&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;whitelist&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; whitelistedClasses&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So there you go, now you don’t have to worry about copy pasting classes around every time you update a list of colors.&lt;/p&gt;
&lt;p&gt;Of course, this is a pretty specific issue to encounter. But the idea behind this post can be applied to many situations.&lt;/p&gt;
&lt;p&gt;Manually managing duplicate data is inefficient and can lead to buggy code if this data isn’t kept in sync. Instead, make sure that your data flows from one place to the other.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-16 { color: #7C4DFF; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-17 { color: #8E8E90; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-5 { color: #A0A1A7F2; }
  .eva-light-bold .grvsc-tldFB4-7 { color: #00BEC4; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-3 { color: #437AED; }
  .eva-light-bold .grvsc-tldFB4-14 { color: #F0AA0B; }
  .eva-light-bold .grvsc-tldFB4-19 { color: #A0A1A7; }
  .eva-light-bold .grvsc-tldFB4-6 { color: #FF6D12; }
  .eva-light-bold .grvsc-tldFB4-11 { color: #C838C6; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-b { font-weight: bold; }
    .grvsc-mm-tEwury .grvsc-tEwury-16 { color: #A78CFA; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-17 { color: #8E99B1; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-6 { color: #79859D; }
    .grvsc-mm-tEwury .grvsc-tEwury-8 { color: #56B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-3 { color: #6495EE; }
    .grvsc-mm-tEwury .grvsc-tEwury-14 { color: #E4BF7F; }
    .grvsc-mm-tEwury .grvsc-tEwury-19 { color: #8792AA; }
    .grvsc-mm-tEwury .grvsc-tEwury-7 { color: #FF9070; }
    .grvsc-mm-tEwury .grvsc-tEwury-12 { color: #CF68E1; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[How to source images and data from JSON files in Gatsby]]></title><description><![CDATA[Use Gatsby's Node API to process images in JSON files with gatsby-plugin-sharp and customize the shape of your data in GraphQL nodes.]]></description><link>https://freddydumont.com/blog/gatsby-source-json-images</link><guid isPermaLink="false">https://freddydumont.com/blog/gatsby-source-json-images</guid><pubDate>Sun, 01 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When I started building the portfolio section of my personal site, I encountered a small problem.&lt;/p&gt;
&lt;p&gt;I was building cards that contained both data and image and I wanted everything to appear under the same GraphQL node. Turns out there’s no way for Gatsby to recognize a path to an image in a JSON file by default.&lt;/p&gt;
&lt;p&gt;Additionally, I needed to do some processing on the tag data before it could be usable so I started searching for a solution that would handle both cases.&lt;/p&gt;
&lt;p&gt;Here’s what a card looks like:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 384px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3e1acaf7fe6a42b1aa43832088e4d248/804b2/portfolio-card-sample.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 146.35416666666666%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAdCAYAAACqhkzFAAAACXBIWXMAAAsTAAALEwEAmpwYAAAF/0lEQVR42p2VeVBV9xXHr0nb6TbpTEycTKd/9C+dCmky8BDC24HHe4/H20HQyvIeZV9cSjCBhGZGJ7GpCFTULiYBwZmg8myn7WRpMu0UE83EduIkmlAQESQqixPQqKyfnnuRpUs6md6Zzz3nd3/nfH/nnvub31V0DhuPplhZbzURnWwmSvihLYnoFAuPpSajczmITbN/OZypKPr4BCpLy9hZXU1huIDamhpCObkUhsIEvT7UebPegClRLyT+B0bN6jVUX9HFxeH1+yktKSEcCpGTl4fX52NLTg52h4PHHn+cWJ2OmNhYzarxukUrxGlWnY9hQ/wGlNq6WiInuvj5gRaea9hLx5EjdHR0cETs0aNHOXbsGJ2dnRrt8ry9vV2jra2NNrH7X3mZV9qP8NuTJ6mrq0Pp6upCvT6fmeGm8P9cs7Ozmo1EIijHjx/XBlNTU8zPzTEjoitRg6enp7k+MsLI6Cjj4zc0/7r42jNh+OpVJm7epFWqXqpwcZUvqmBoeJihK8NcGf6U3v4Bzvf00nvxEgOXB+m7dJmrI6McPnwYRS1TvXp7+7hw/mP6+i5yeXCQQWFgYIDR0THmpPLBoSuMjI0zPjHB0NBFeno+ZOzGDSYnJ/l8cpTpmdt0SJ+XBPv6+7nwSY/2GtPTU9y5c4fbwp27d2U8w4QkfnLhPGf+/A67WnspavyQ+l37ef1P71La0kPdgQ/YU/GkvPI9wfGr1/js2nWGBofov3RJKhriU+mN2rPZuVlu35qm+hevUxLpYFPzWeK3f8yj1hoO/KaL5Pp/kJD7NoWG4HKFnR+cpeXdvzI4NsaMfKAp+RBa/+bmNfuH7nN8w3sUf/drnBseYVvjR6zW7aapqZGP+q5hLOrGbipeFnzpb2fY3f0Ww5MT2nh+fv6e1e5Utr2KEv8SvsLnGBno4/nWPr4Zd5DWSLf0bwZL8SnWbShBOXHihJY4c28Pql905ZbRxrPTnDx7mj3tZ3i2IcLv33yLc73X+XWkh0Odp+npH+Kdc+NU1h38cttmsdTbszPcnZ9jQhZTffW6desWn01MLm/s8vJyWlpa2LdvH42Njf8Vda6hoYEmdbyvkSYZN9173tzcLDFNmkZFRQVKTEwMa9eu1Vi3bt2/2EV+sH490dHRrI+KIkpF/KhFX0PmJEbVUgwGA6FQPgXhkJw2+RqbNmURCocpLCrWcLvdGOQIs5gtmE1mzGbBYlnCZDJpGI1GlEQ5x+xODy53AKfLR7ong1SHm4DXjd/j0kiRQ3dDQizxCToSEgU5phJixeriiI+NQ6/XL6EkPKHnqRI9B+sNNNcaaHlGz08rniA5r5q06v04tv0Mm72AH6XuJtezh0zzs3hDBRT88gVCLc+Tu+cZ1LdcFkzQ88dDyfy908KpNgvvv2rlLy9byXoxQtav3mNz63sEspvZW/Y+h+rPU5vxNqFdL1Jz4Q2qTv+OmlMnMVnMJMpprQmqr5xkNeJymAULaXYTVosc+ZYkrHYXpuRUjGp/9EnoE5LESs+khylOO7Y0B9aUZIxSocG4UKWi3lwuj/TRTbLNiTXJjjXZhs/jJt2RijPVRjAYwBfw4A96cMiPyJaSIgs7ZM6OJ91NMCNDNFxalYp6czqd+OQ/EggE8Hi82O12LcDr9WKz2cT6SBERv38hJlWE0tLSJNajoeaqGguCJiN2twtPRkB6tRFvZgYOdzpOqdCbEcQd8OPwpOPyebVxuoi6BDUmmJ0l8UGcsiNUjUR12yRt9OGtLCSrporMJ8sJ7ijDv70E39YiGVeQ/fQ2bbyxppLN4qvzGdXleLcWk72zioyflBPYXoq7LExSlh/loaLNPFyRx5qqEA9X5gt5Go9sK5DnuTxYukXm8lldlsNDghqzRnhka3g5Vvw1orG6cBPKfT/OQglnrmAjSoGQH+TrVXk88FQJXxOhB3YW82B9BV8t24ISyvy3nBV5XynMRhW9T7UrfDXoOzVFfO+Far69o4Dv7t7B9/c+zbe2h1kliUvxS3kLucr9clv1RaiV5AUXVldtbkBbaNX/yPkn+BOql6TUR0gAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;portfolio card sample&quot;
        title=&quot;&quot;
        src=&quot;/static/3e1acaf7fe6a42b1aa43832088e4d248/804b2/portfolio-card-sample.png&quot;
        srcset=&quot;/static/3e1acaf7fe6a42b1aa43832088e4d248/8514f/portfolio-card-sample.png 192w,
/static/3e1acaf7fe6a42b1aa43832088e4d248/804b2/portfolio-card-sample.png 384w&quot;
        sizes=&quot;(max-width: 384px) 100vw, 384px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;I wanted to achieve two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Process the tag array and turn it into a usable array of objects&lt;/li&gt;
&lt;li&gt;Have Sharp process the image and get the resulting node in the same tree&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My first reflex was to go for &lt;a href=&quot;https://www.gatsbyjs.org/packages/gatsby-transformer-json/&quot;&gt;&lt;code&gt;gatsby-transformer-json&lt;/code&gt;&lt;/a&gt;, but I quickly realized that this transformer wouldn’t understand that I had image paths in my data, much less process them using Sharp.&lt;/p&gt;
&lt;p&gt;On top of it, I couldn’t preprocess my tag array nor give a specific shape to my &lt;code&gt;PortfolioCard&lt;/code&gt; node.&lt;/p&gt;
&lt;p&gt;Back to the docs. I knew that I needed to source images from the filesystem so I started there. The &lt;a href=&quot;https://www.gatsbyjs.org/docs/working-with-images/&quot;&gt;documentation&lt;/a&gt; was helpful in that regard.&lt;/p&gt;
&lt;p&gt;I recommend you have a read if you’ve never done this, but here’s a summary of what you need to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install the following plugins with yarn:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;shell&quot; data-index=&quot;0&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;yarn add gatsby-image gatsby-source-filesystem gatsby-plugin-sharp gatsby-transformer-sharp&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or using &lt;code&gt;npm&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;shell&quot; data-index=&quot;1&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;npm install gatsby-image gatsby-source-filesystem gatsby-plugin-sharp gatsby-transformer-sharp&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Add the plugins to your &lt;code&gt;gatsby-config.js&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;grvsc-container grvsc-has-line-highlighting eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;javascript&quot; data-index=&quot;2&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`path`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  plugins: &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`gatsby-transformer-sharp`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`gatsby-plugin-sharp`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`gatsby-source-filesystem`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`images`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; provide the path to your image folder here:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; path&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;__dirname&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`src`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`assets`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;!-- prettier-ignore --&gt;
&lt;p&gt;Now if you go to your GraphiQL IDE (by default Gatsby serves it on &lt;a href=&quot;http://localhost:8000/___graphql&quot;&gt;http://localhost:8000/___graphql&lt;/a&gt;) you’ll see an &lt;code&gt;allImageSharp&lt;/code&gt; field which you can use to query your images processed by Sharp.&lt;/p&gt;
&lt;p&gt;That’s great, but not exactly what I wanted.&lt;/p&gt;
&lt;p&gt;These image nodes should appear under the corresponding &lt;code&gt;PortfolioCard&lt;/code&gt; tree with the rest of the associated data. Not on a top-level node that I would have to query separately using the name of the image obtained in a previous query.&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;With some research, I ended up on &lt;a href=&quot;https://www.gatsbyjs.org/docs/node-apis/#sourceNodes&quot;&gt;&lt;code&gt;sourceNodes&lt;/code&gt; in the Gatsby Node APIs docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I understood that using this API, I could grab data from the filesystem, transform it and build a node in the shape that suited my needs.&lt;/p&gt;
&lt;p&gt;Here’s the result of my work in a step-by-step format. I’ll be using code examples from my own website so feel free to browse the &lt;a href=&quot;https://github.com/freddydumont/freddydumont.com/blob/master/gatsby-node.js&quot;&gt;source code on GitHub&lt;/a&gt; should you need to.&lt;/p&gt;
&lt;h3&gt;Sourcing the data&lt;/h3&gt;
&lt;p&gt;The first thing we need to do is to get the JSON data in the &lt;code&gt;gatsby-node.js&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;If we were to write a plugin handling a number of use cases, we’d need to read from the filesystem using Node to source the data. But because we’re targeting a specific use case, hard coding the paths to our JSON files is the simplest thing to do:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;javascript&quot; data-index=&quot;3&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;path&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;portfolio&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;./src/data/portfolio.json&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;./src/data/tag_colors.json&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; relative path from `gatsby-node.js`&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;IMAGE_PATH&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;./src/assets/&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For reference, here’s &lt;code&gt;portfolio.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;json&quot; data-index=&quot;4&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;NessIA.ca&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;category&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;Business website&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;description&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;Built for a client operating in the Business Intelligence space...&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;technology&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;The tool of choice these days for blazing fast websites is Gatsby...&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;link&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;https://nessia.ca/en&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;image&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;nessia.png&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;alt&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;NessIA homepage&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;tags&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;react&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;gatsby&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;bulma&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;},&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and &lt;code&gt;tag_colors.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;json&quot; data-index=&quot;5&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;bg-yellow-vivid-400 text-gray-900&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;react&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;bg-blue-400 text-gray-900&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;gatsby&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;bg-purple-700 text-purple-100&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that we have access to our data, we need to build a node by adapting the &lt;a href=&quot;https://www.gatsbyjs.org/docs/node-apis/#sourceNodes&quot;&gt;code example from the docs&lt;/a&gt; to fit our objects.&lt;/p&gt;
&lt;p&gt;In this example, we need to iterate over an array and build a node for each portfolio card, so all our code will be written inside a &lt;code&gt;forEach&lt;/code&gt; loop:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container grvsc-has-line-highlighting eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;javascript&quot; data-index=&quot;6&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;sourceNodes&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;({ &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;createNodeId&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;createContentDigest&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; })&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  portfolio&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; 1. Extract the card data.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;technology&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; 2. Build the PortfolioCard node. Note that most fields simply correspond to&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;    to our JSON data.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;technology&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; &amp;lt;----- Problem here&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; &amp;lt;------ and here&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;createNodeId&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`card-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tEwury-3&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tEwury-3&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;PortfolioCard&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;contentDigest&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;createContentDigest&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; 3. Create the node&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;createNode&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s a good start. Our data now exists in a top-level node called &lt;code&gt;allPortfolioCard&lt;/code&gt;. But what happens if we query it?&lt;/p&gt;
&lt;pre class=&quot;grvsc-container grvsc-has-line-highlighting eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;graphql&quot; data-index=&quot;7&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;query &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  allPortfolioCard &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    nodes &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      alt&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      category&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      description&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      image&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      link&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      tags&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      technology&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      title&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As expected, we have a couple of issues. Both &lt;code&gt;image&lt;/code&gt; and &lt;code&gt;tags&lt;/code&gt; need to be processed before they can be usable.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container grvsc-has-line-highlighting eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;json&quot; data-index=&quot;8&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;allPortfolioCard&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;nodes&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;alt&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;NessIA homepage&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;category&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;Business website&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;description&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;Built for a client operating in the Business Intelligence space...&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;image&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;nessia.png&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;link&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;https://nessia.ca/en&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;tags&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;react&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;gatsby&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;bulma&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;],&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;technology&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;The tool of choice these days for blazing fast websites is Gatsby...&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;title&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;NessIA.ca&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;},&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s start with our &lt;code&gt;tags&lt;/code&gt; array.&lt;/p&gt;
&lt;h3&gt;Processing JSON data&lt;/h3&gt;
&lt;p&gt;Right now, we have an array of strings that needs to be transformed into an array of objects with the properties &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;color&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;json&quot; data-index=&quot;9&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;color&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;bg-yellow-vivid-400 text-gray-900&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;},&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a fairly simple fix. We have the color data already imported in our &lt;code&gt;gatsby-node.js&lt;/code&gt; file. All we need to do is to &lt;code&gt;map&lt;/code&gt; over the tag array when building our node and return an object:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container grvsc-has-line-highlighting eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;javascript&quot; data-index=&quot;10&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;./src/data/tag_colors.json&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;sourceNodes&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;({ &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;createNodeId&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;createContentDigest&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; })&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  portfolio&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;({&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}))&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;createNode&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following query will now be possible:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;graphql&quot; data-index=&quot;11&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;query &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  allPortfolioCard &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    nodes &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      tags &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        color&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        name&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And will return the following data:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container grvsc-has-line-highlighting eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;json&quot; data-index=&quot;12&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;allPortfolioCard&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;nodes&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;tags&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;[&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;color&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;bg-yellow-vivid-400 text-gray-900&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;javascript&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;},&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;color&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;bg-blue-400 text-gray-900&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;              &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;quot;react&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, when building a custom node, we can do whatever we want with the data. We can pass it down as it is or transform it into a desired shape.&lt;/p&gt;
&lt;p&gt;But what about the image field?&lt;/p&gt;
&lt;h3&gt;Transforming an image path into a &lt;code&gt;childImageSharp&lt;/code&gt; node&lt;/h3&gt;
&lt;p&gt;Now that’s the tricky part.&lt;/p&gt;
&lt;p&gt;It took a bit of digging, but I learned from &lt;a href=&quot;https://stackoverflow.com/a/56012718&quot;&gt;this Stack Overflow answer&lt;/a&gt; that in order for Sharp to transform an image, it needs to be a &lt;code&gt;File&lt;/code&gt; node.&lt;/p&gt;
&lt;p&gt;So all we need to do is to create such a node by giving it the required fields.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;gatsby-transformer-sharp&lt;/code&gt; only checks if a node has the field ‘extension’ and — if it is one of the valid file types — processes it.
&lt;cite&gt;&lt;a href=&quot;https://stackoverflow.com/users/10340970/derek-nguyen&quot;&gt;Derek Nguyen&lt;/a&gt; on &lt;a href=&quot;https://stackoverflow.com/a/56012718&quot;&gt;Stack Overflow&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The whole answer is worth reading to deepen your understanding of how Gatsby operates under the hood. Let’s go ahead and implement it.&lt;/p&gt;
&lt;pre class=&quot;grvsc-container grvsc-has-line-highlighting eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;javascript&quot; data-index=&quot;13&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;exports&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;sourceNodes&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;({ &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;createNodeId&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;createContentDigest&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt; })&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  portfolio&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;card&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; 1. name, extension and absolute path are required to build a File node&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;absolutePath&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;__dirname&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;IMAGE_PATH&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; 2. Build a data shape that corresponds to a File node that Sharp can process&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;absolutePath&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; &amp;lt;-- required&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;ext&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; &amp;lt;-- required, remove the dot in `ext`&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; 3. Build the image node using our data&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;imageNode&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;createNodeId&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`card-image-&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tEwury-3&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tEwury-3&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;internal&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-10 grvsc-tEwury-11&quot;&gt;&amp;#39;PortfolioCardImage&amp;#39;&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;contentDigest&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;createContentDigest&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; 4. Create the node. When imageNode is created,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt;    Sharp adds childImageSharp to the node&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;createNode&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;imageNode&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;const &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; 5. Add the image node to our tree&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line grvsc-line-highlighted&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-12 grvsc-tEwury-13&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;imageNode&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-5 grvsc-tEwury-6&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tldFB4-i grvsc-tEwury-5 grvsc-tEwury-i&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-16 grvsc-tldFB4-b grvsc-tEwury-16&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    actions&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tldFB4-b grvsc-tEwury-9 grvsc-tEwury-b&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-3 grvsc-tldFB4-b grvsc-tEwury-3&quot;&gt;createNode&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;})&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-4 grvsc-tEwury-5&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when you go back to your Graph Explorer, your should see a &lt;code&gt;childImageSharp&lt;/code&gt; node under the &lt;code&gt;image&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;You can then query for it and use it in conjunction with &lt;code&gt;gatsby-image&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here’s what the final query looks like:&lt;/p&gt;
&lt;pre class=&quot;grvsc-container eva-light-bold grvsc-mm-tEwury&quot; data-language=&quot;graphql&quot; data-index=&quot;14&quot;&gt;&lt;code class=&quot;grvsc-code&quot;&gt;&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;query &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  allPortfolioCard &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    nodes &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      image &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        childImageSharp &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          fluid&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt;maxWidth&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-17 grvsc-tEwury-17&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-14 grvsc-tEwury-14&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-6 grvsc-tEwury-7&quot;&gt;384&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt; &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;            &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-7 grvsc-tEwury-8&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;GatsbyImageSharpFluid&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;          &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      alt&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      category&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      description&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      technology&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      link&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      tags &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        color&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;        name&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;      title&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;    &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-1 grvsc-tEwury-4&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;grvsc-line&quot;&gt;&lt;span class=&quot;grvsc-source&quot;&gt;&lt;span class=&quot;grvsc-tldFB4-8 grvsc-tEwury-9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gatsby is pretty powerful out of the box, and even more once you start understanding how it works. I’m still barely scratching the surface but the more I play with it, the more I’m amazed with what it can do.&lt;/p&gt;
&lt;p&gt;The key takeaway here is that whether you need to process your data before sending it to your GraphQL tree, or you need images processed by &lt;code&gt;gatsby-plugin-sharp&lt;/code&gt; under a specific node, using the &lt;code&gt;sourceNodes&lt;/code&gt; API in conjunction with the &lt;code&gt;createNode&lt;/code&gt; action will help you achieve your goals.&lt;/p&gt;
&lt;style class=&quot;grvsc-styles&quot;&gt;
  
  .eva-light-bold {
    background-color: #EBEEF5;
    color: #5D5D5F;
  }
  .eva-light-bold .grvsc-tldFB4-i { font-style: italic; }
  .eva-light-bold .grvsc-tldFB4-b { font-weight: bold; }
  .eva-light-bold .grvsc-tldFB4-1 { color: #5D5D5F; }
  .eva-light-bold .grvsc-tldFB4-16 { color: #7C4DFF; }
  .eva-light-bold .grvsc-tldFB4-8 { color: #5D5D5FD8; }
  .eva-light-bold .grvsc-tldFB4-3 { color: #437AED; }
  .eva-light-bold .grvsc-tldFB4-10 { color: #53A053; }
  .eva-light-bold .grvsc-tldFB4-4 { color: #A9A9AA; }
  .eva-light-bold .grvsc-tldFB4-7 { color: #00BEC4; }
  .eva-light-bold .grvsc-tldFB4-5 { color: #A0A1A7F2; }
  .eva-light-bold .grvsc-tldFB4-12 { color: #CD6069; }
  .eva-light-bold .grvsc-tldFB4-17 { color: #8E8E90; }
  .eva-light-bold .grvsc-tldFB4-14 { color: #F0AA0B; }
  .eva-light-bold .grvsc-tldFB4-6 { color: #FF6D12; }
  .eva-light-bold .grvsc-line-highlighted::before {
    background-color: var(--grvsc-line-highlighted-background-color, rgba(0, 0, 0, 0.05));
    box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(0, 0, 0, 0.2));
  }
  
  /* Eva Dark */
  @media (prefers-color-scheme: dark) {
    .grvsc-mm-tEwury {
      background-color: #282c34;
      color: #9DA5B3;
    }
    .grvsc-mm-tEwury .grvsc-tEwury-i { font-style: italic; }
    .grvsc-mm-tEwury .grvsc-tEwury-b { font-weight: bold; }
    .grvsc-mm-tEwury .grvsc-tEwury-4 { color: #B0B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-16 { color: #A78CFA; }
    .grvsc-mm-tEwury .grvsc-tEwury-9 { color: #838FA7; }
    .grvsc-mm-tEwury .grvsc-tEwury-3 { color: #6495EE; }
    .grvsc-mm-tEwury .grvsc-tEwury-11 { color: #98C379; }
    .grvsc-mm-tEwury .grvsc-tEwury-5 { color: #676E95; }
    .grvsc-mm-tEwury .grvsc-tEwury-8 { color: #56B7C3; }
    .grvsc-mm-tEwury .grvsc-tEwury-6 { color: #79859D; }
    .grvsc-mm-tEwury .grvsc-tEwury-13 { color: #E06C75; }
    .grvsc-mm-tEwury .grvsc-tEwury-17 { color: #8E99B1; }
    .grvsc-mm-tEwury .grvsc-tEwury-14 { color: #E4BF7F; }
    .grvsc-mm-tEwury .grvsc-tEwury-7 { color: #FF9070; }
    .grvsc-mm-tEwury .grvsc-line-highlighted::before {
      background-color: var(--grvsc-line-highlighted-background-color, rgba(255, 255, 255, 0.1));
      box-shadow: inset var(--grvsc-line-highlighted-border-width, 4px) 0 0 0 var(--grvsc-line-highlighted-border-color, rgba(255, 255, 255, 0.5));
    }
  }
&lt;/style&gt;</content:encoded></item><item><title><![CDATA[Master Git, not the command line]]></title><description><![CDATA[Learning Git isn't about memorizing commands. It's about understanding version control. I suggest using a GUI instead of the CLI to do that.]]></description><link>https://freddydumont.com/blog/git-cli-vs-gui</link><guid isPermaLink="false">https://freddydumont.com/blog/git-cli-vs-gui</guid><pubDate>Sat, 12 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Learning Git isn’t about memorizing commands. It’s about &lt;strong&gt;understanding&lt;/strong&gt; version control. I suggest using a graphical user interface (GUI) instead of a command line interface (CLI) to do that.&lt;/p&gt;
&lt;p&gt;The idea that using the command line exclusively makes you a better developer, or worse, a &lt;em&gt;real developer™&lt;/em&gt; , is misleading for newbies and even for more experienced developers. I’ve seen people from all levels carry this assumption, and I’ve seen this assumption limit the developer’s abilities every time.&lt;/p&gt;
&lt;p&gt;Such a thought, when religiously adhered to by developers, is limiting their ability to do great work, to seek the best solution to the problem and to avoid basic mistakes. This can be adapted to any other area where developers tend to become zealous, but in my experience, Git and version control has been a good example.&lt;/p&gt;
&lt;p&gt;That’s because there are amazing GUI tools for Git. I personally use &lt;a href=&quot;https://www.gitkraken.com/invite/qTgsLk53&quot;&gt;GitKraken&lt;/a&gt; but any Git client with the same features would do the trick.&lt;/p&gt;
&lt;p&gt;The main difference between the command line and a GUI is &lt;em&gt;usability&lt;/em&gt;. Let’s examine using screenshots.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/333ce25117e27ac88a498e59751a86be/49b61/git_cli.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 86.45833333333334%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAABYlAAAWJQFJUiTwAAADkklEQVR42lVTWU9TURDub9BXEwMilq2lQOlCKd3uVrhtb8stdK8tLTuoGMMWcHnwwRf3RhP/A/HFGAIupIJoIKj/hpszzpyi4sPkzD33nG++75s5prcf6uYvX49eHR39qh1+/1k7qJ/UDvZPars79dr2+8+1ne065vu47v3NP+4e4JljHvv149rhwY+Xe5++vd7aenfNFI0uSqX8fShl7rJRbZkNBxYgHl6C/NgGZEfXIKevQzqxCrnkOqTiK5DBPJNYg5S2AuOxZUjjXkS8yTBA9JYFk9s6JoreCtiu6aedzfHTriua0ds+ZiiBaUMYrBji0KQR8kzwPDhQNkRvledOa9qwtiYMy9W40dUSP7W0xMHcpAgmnysnJSNLMNRfZPbOcYZg4OnNQVSah6g8D4mRGzAcmoGYsgDDwRnwO6/DQE8enNYM9HWMAZ3HwLsp6DZrgmnQnpNi8iLI/ikmeCqsr2Mc3LYcqOIcB4hKcxBD4BFhFiVVIYx7tB8OTIO3vwievjwE3CXm6MpAZ4sqmOyWpEQHkAXz2ousvysFzu4Mv4BSQfZNgjhUBRUBA+5y4xuBQwMT4EO2zu4sZ9jXgQxbtIZkPXIL/K4Ss7aOsp62JErKccAIshxBuSSdgvI/LCUsogSmIIjAaBFXxgFDg0WJLqLRzGFJc0A3Ao6qNzkgeRkmD+UFiKB8YhhHX8kGvAMCNhTZorI0AqoNhthROkhyOSAZTmDEguygiyr3sMKZo98gITA1iGwgdQ7LmYcEyDsYmmXoHbOZkyghz7tKMgnY7yo32GAQAHlLRanD1N3/PBxEQA0vK/5JhkAckDwkWSSRxuYPKxEBFVzPs6UC6N85QHtKoh8ojdHoUFWqHg5Oc1k0g7T6HNc5QxdOADYPyBpqRG97Emxm/X/AYd69CsO5QoY6zmGWS6Zu8rVRkDeIOi15J89GJgMuPOu1F3Cwz5oS8hUlxT9NPxCwwGioSTJ1laQlzqRT0Ygwx3MqRN7KvqlGLs0xpzULnU0IKIUm+GAjK4Zy+GATw5HQLGdEL6Yxg7M8J4bEjgLHDC3IgsOa/ic5GV+UNlefw6OHb9i99Rds5fZjWLvzBNaXn8LGyrlYfQZ3157Dg80aTJXu82eHiujZ0VRwwMbTs8cuquGqrOvzMq0e+7gc9BQwsvhdlsPBgqyJVVlT8Yw2I6timf9z2nQ8q/O19bIotzUrSvsl8cJvQ2F0KITZSH4AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;list of git commands on the CLI&quot;
        title=&quot;&quot;
        src=&quot;/static/333ce25117e27ac88a498e59751a86be/e5715/git_cli.png&quot;
        srcset=&quot;/static/333ce25117e27ac88a498e59751a86be/8514f/git_cli.png 192w,
/static/333ce25117e27ac88a498e59751a86be/804b2/git_cli.png 384w,
/static/333ce25117e27ac88a498e59751a86be/e5715/git_cli.png 768w,
/static/333ce25117e27ac88a498e59751a86be/4ad3a/git_cli.png 1152w,
/static/333ce25117e27ac88a498e59751a86be/71c1d/git_cli.png 1536w,
/static/333ce25117e27ac88a498e59751a86be/49b61/git_cli.png 1756w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here’s the interface when you use autocompletion on the command line. A list of all possible Git commands. Not so user friendly.&lt;/p&gt;
&lt;p&gt;What ends up happening is that, rather than learning Git concepts, you end up memorizing specific commands and stay at the most basic level. Pull, push, branch, add, commit. That’s pretty much it.&lt;/p&gt;
&lt;p&gt;But Git is more powerful than that. The issue with the command line is that you don’t see what’s available.&lt;/p&gt;
&lt;p&gt;That &lt;em&gt;“Fix typo”&lt;/em&gt; commit could have been &lt;strong&gt;squashed&lt;/strong&gt; with the previous one before pushing. That &lt;em&gt;“Saving changes”&lt;/em&gt; commit is cluttering the git history and is meaningless to anybody else but you at that exact point in time (try remembering what that commit was about a month later). &lt;strong&gt;Stashing&lt;/strong&gt; your work in progress is what you’re most likely trying to do.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 768px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d69ac4c65c5ce58fe330a828e2f6d7ad/d9ed5/git_gui.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 62.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAAsTAAALEwEAmpwYAAACcElEQVR42m1U2XKbQBDkN2LrQAKJc7lZECAOHZYtO0mlKpX8/5d0eleynVTlYWrEDvT0sSWj7S8odyecrz9xvPxAUR+QyRFVe0bZnJBVE1I+q64q51xkHcK0RcQeFT2SekLFd9W5cSLIfrhy2GIb5rA2Ama6R9g8I857BNkAL1F9RFQedC/kEW13QTu84vz2G/3rL+ynr9iPrzDa0xl518GVLaxqxGIb4DEc4BdklpUQcYE4lYiSEn6Ywlw7PJPopxcN0I3f0PRnDC9XtIcnGM10Qlw1sN0ID7MV5vMlFsEEj8zStNAgrh/DCxKsLAdLc4OtF9OWDllJ2WkFuRtohbJBwqibIwJRYm2T2XyN+cKCGR7gJjvEScGPBW3wYdkeZpw9cul6E8DxEs4SOL5amMHeRvBomTEer0jLPWwnIiAZLmxY4oiw6FDKBlmxQ5JJMk0wX9p6qe0IMtshpHSfZG5V8LmGMZ3eULcnuEF+k0zAdUTWeYu8qBlAQ89yKvDugCts3Zip8jaUHURSf1ScNTDkbkRatKQcfkheiREOt4so12FsnECDacl8x1KS/XfJyf13rJkaSqoamGv3H0A3qbSHil0YKY9uHn8Cpvq799qQtfZQmxpkTND7ALTukkOR8tzV6S7Mm3/L1YZ+5pQ8oagGLVP5mVK+suE/gPRQ8ALThiyX+tqElO54VOIKMvV1wrIeeS55nXIyKxBwiQI23qNfrhx8eTR1MA9zi32tA5jdZSr/lI8PM5MSI8j9hJiBRSXDYAUMUPBGGCp6VYql8uFW0Wc5f3WWvRUIEwnZH9AdL5iev6Pn/8C3pwPG4xP+AGHlkl/KcefOAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Git graphical user interface&quot;
        title=&quot;&quot;
        src=&quot;/static/d69ac4c65c5ce58fe330a828e2f6d7ad/e5715/git_gui.png&quot;
        srcset=&quot;/static/d69ac4c65c5ce58fe330a828e2f6d7ad/8514f/git_gui.png 192w,
/static/d69ac4c65c5ce58fe330a828e2f6d7ad/804b2/git_gui.png 384w,
/static/d69ac4c65c5ce58fe330a828e2f6d7ad/e5715/git_gui.png 768w,
/static/d69ac4c65c5ce58fe330a828e2f6d7ad/4ad3a/git_gui.png 1152w,
/static/d69ac4c65c5ce58fe330a828e2f6d7ad/71c1d/git_gui.png 1536w,
/static/d69ac4c65c5ce58fe330a828e2f6d7ad/d9ed5/git_gui.png 2880w&quot;
        sizes=&quot;(max-width: 768px) 100vw, 768px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here’s the difference when using a Git GUI with an intuitive interface. What’s interesting is that only &lt;em&gt;relevant commands&lt;/em&gt; are displayed. And the interface adapts to your actions. It shows you what commands are available for a specific commit, or for a list of commits, or for a single file, or for many files… you get the idea.&lt;/p&gt;
&lt;p&gt;What’s more is that it even makes you a better CLI Git user. Why? Because a GUI like GitKraken doesn’t hide anything from you. The command names are the same as the CLI. Not only will you learn Git &lt;em&gt;concepts&lt;/em&gt; but you’ll learn the commands.&lt;/p&gt;
&lt;p&gt;I won’t go on about what the GUI can do so if you want to know more, check out &lt;a href=&quot;https://www.gitkraken.com/git-client&quot;&gt;GitKraken’s feature page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The purpose of this post was to show you that dogmatism in software development rarely leads to effectiveness and efficiency and to point out that what’s most important is to &lt;strong&gt;understand&lt;/strong&gt; what you’re doing before trying to memorize how to do it.&lt;/p&gt;
&lt;p&gt;If you want to start using GitKraken, feel free to use my &lt;a href=&quot;https://www.gitkraken.com/invite/qTgsLk53&quot;&gt;invite link&lt;/a&gt;.&lt;/p&gt;</content:encoded></item></channel></rss>