<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: egghead.io</title>
    <description>The latest articles on DEV Community by egghead.io (@egghead).</description>
    <link>https://dev.to/egghead</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F481%2Fa9cbe1bb-1763-4fa5-957f-a7578933cd22.png</url>
      <title>DEV Community: egghead.io</title>
      <link>https://dev.to/egghead</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/egghead"/>
    <language>en</language>
    <item>
      <title>Content Modeling and Data Design with Sanity.io</title>
      <dc:creator>Joel Hooks 🌩</dc:creator>
      <pubDate>Sun, 07 Feb 2021 05:00:25 +0000</pubDate>
      <link>https://dev.to/egghead/content-modeling-and-data-design-with-sanity-io-1l6j</link>
      <guid>https://dev.to/egghead/content-modeling-and-data-design-with-sanity-io-1l6j</guid>
      <description>&lt;p&gt;Data is the bedrock of most applications and challenging to get right. It's&lt;br&gt;
  vital for the long-term health of an application to have data that is&lt;br&gt;
  understandable, flexible, and relevant to users.&lt;/p&gt;

&lt;p&gt;This article discusses how we are reconsidering content modeling at a data level for egghead to allow for more flexible collaboration and design.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where we were at when this started
&lt;/h2&gt;

&lt;p&gt;We have two APIs available that access the same underlying postgres database. One is a REST API that has nice hypermedia capabilities and is pretty slow built on top of jbuilder. The other is a well-formed GraphQL API that is much more performant and provides immediate and direct access to slices of the data we want.&lt;/p&gt;

&lt;p&gt;Both of these APIs are fairly 1:1 with the underlying database and define what I've come to look at as a "hyper specified" content model that got the job done, but has been a serious constraint that we've had to live with for many years 😅&lt;/p&gt;

&lt;p&gt;Both of these APIs take 30-40 minutes to deploy and require fairly deep knowledge of Rails, GraphQL, and Ruby.&lt;/p&gt;

&lt;p&gt;The deeply technical API layer provides a massive barrier to entry, and because everything is very closely tied to the database changes can have rippling side-effects that were never intended&lt;/p&gt;
&lt;h3&gt;
  
  
  Resources and Collections
&lt;/h3&gt;

&lt;p&gt;When egghead was a sparkling fresh app I started out modeling what was obvious at the time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Lessons&lt;/code&gt;: people come to egghead to watch lessons, not videos.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Series&lt;/code&gt;: sometimes people want to watch a series of lessons (not videos)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Later we added &lt;code&gt;Playlists&lt;/code&gt;, called them collections, and then decided that both a series and a playlist were actually courses. lol&lt;/p&gt;

&lt;p&gt;Fundamentally what we are dealing with are &lt;strong&gt;resources&lt;/strong&gt; and &lt;strong&gt;collections&lt;/strong&gt;, where a collection is just a resource with a list of other resources referenced.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where we want to be
&lt;/h2&gt;

&lt;p&gt;We want to build &lt;a href="https://joelhooks.com/digital-garden"&gt;a digital garden&lt;/a&gt;. We want to curate, update, associate, and present relevant resources to users so that they can quickly find what they need and reach the outcomes they desire.&lt;/p&gt;

&lt;p&gt;For us, this requires a "metadata" layer that sits above the APIs without a rigid schema or the need for deep levels of technical know-how to operate.&lt;/p&gt;

&lt;p&gt;For years we've done this by creating various JavaScript/JSON data structures in flat files that get compiled in with the application.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://github.com/eggheadio/egghead-next/blob/83629b66ad705cc3a0224a54baa3aa3c8e755d9c/src/data/courseDependenciesData.js"&gt;see an example here where we keep various metadata about egghead courses&lt;/a&gt;. Another example is &lt;a href="https://github.com/eggheadio/egghead-next/blob/83629b66ad705cc3a0224a54baa3aa3c8e755d9c/src/components/pages/home/homepage-data.ts"&gt;this one that describes the data for our curated home page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This honestly isn't bad, but it's still tedious, error-prone, and requires us to use IDEs as UI for updating content. Not ideal, but very flexible.&lt;/p&gt;
&lt;h2&gt;
  
  
  Content Modeling with Sanity.io
&lt;/h2&gt;

&lt;p&gt;After exploring several alternatives and living with the flat json files for years, a product has emerged that checks most of the boxes that we need. Deep flexibility. Ease of use. An incredibly nice authoring experience and a welcoming team and community.&lt;/p&gt;

&lt;p&gt;Sanity.&lt;/p&gt;

&lt;p&gt;What first stands out to me about Sanity is that it installs into your project via their CLI (command line interface). This was so different to me that it was hard to understand at first, but once I got the CLI installed with a default starter dataset, it started to make sense.&lt;/p&gt;

&lt;p&gt;Once I read the docs and started to explore the flexible potential it really clicked.&lt;/p&gt;

&lt;p&gt;The core atom of our emerging system is the &lt;code&gt;resource&lt;/code&gt; type. You can see &lt;a href="https://github.com/eggheadio/egghead-next/commits/main/studio/schemas/resource.js"&gt;how it evolved here on Github&lt;/a&gt; if you're interested.&lt;/p&gt;

&lt;p&gt;A resource as a &lt;code&gt;type&lt;/code&gt; property. It can describe any of the content types that we deal with at egghead, and we can always add additional types as needed. Types are resources like podcasts, videos, courses, collections, and features.&lt;/p&gt;

&lt;p&gt;We are able to sync our existing database with Sanity, so all of the items that are in postgres are represented in Sanity. It could potentially replace a huge portion of our data needs, but for now, we are keeping them synced manually.&lt;/p&gt;
&lt;h3&gt;
  
  
  Modeling resources, not layout
&lt;/h3&gt;

&lt;p&gt;What's stood out as important for us is that we want to create a robust resource model that describes and augments core content. We want it to be flexible and longer-lived than a typical layout and withstand heavy changes to design and presentation.&lt;/p&gt;

&lt;p&gt;We want our data to work in different contexts across our app (and beyond) so we are intentionally avoiding any layout concerns in the content model. The data describes what, not where, the content will ultimately be displayed.&lt;/p&gt;

&lt;p&gt;This means that instead of modeling pages, we are describing content and layering on supporting assets and metadata that allow us to choose content that's appropriate and relevant, when and where you need it.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Practical Example
&lt;/h2&gt;

&lt;p&gt;To illustrate, on the egghead homepage we present a large banner that showcases a resource, a new course, an evemt, etc&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4f1-jkTG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dg3gyk0gu/image/upload/c_scale%2Cw_1122/v1612670503/egghead-next-ebombs/egghead-content-modeling-with-santity-io/screenshot%2520of%2520egghead%2520home%2520page%2520with%2520cloudflare%2520workers%2520course%2520presented.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4f1-jkTG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/dg3gyk0gu/image/upload/c_scale%2Cw_1122/v1612670503/egghead-next-ebombs/egghead-content-modeling-with-santity-io/screenshot%2520of%2520egghead%2520home%2520page%2520with%2520cloudflare%2520workers%2520course%2520presented.jpg" alt="screenshot of egghead home page with cloudflare workers banner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's represented in &lt;a href="https://github.com/eggheadio/egghead-next/blob/83629b66ad705cc3a0224a54baa3aa3c8e755d9c/src/components/pages/home/homepage-data.ts#L2-L20"&gt;&lt;code&gt;home-page-date.ts&lt;/code&gt; as a property called &lt;code&gt;jumbotron&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jumbotron&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Introduction to Cloudflare Workers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;byline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new course&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;`Follow along with Kristian Freeman as you build a localization engine that
     renders data based on the Edge location nearest to the application's user.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/banner.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/playlists/introduction-to-cloudflare-workers-5aa3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;introduction-to-cloudflare-workers-5aa3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;instructor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Kristian Freeman&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kristian-freeman&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/q/resources-by-kristian-freeman&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;twitter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;signalnerve&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/headshot.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works pretty well. When we want to swap it out we just go in and enter new data. It's not great though, and requires a deploy.&lt;/p&gt;

&lt;p&gt;When I sat down to model this in Sanity, the first impulse was to create a document called "Home Page Jumbotron". Then I can query Sanity using their GROQ query language on the server and create a similar data structure to render the header graphic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetServerSideProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sanityClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;groq&lt;/span&gt;&lt;span class="s2"&gt;`
      *[slug.current == 'home-page-jumbotron'][0]{
        name,
        title,
        description,
        summary,
        byline, 
        meta,
        path,
        'slug': resources[][0]-&amp;gt;_id,
        'instructor': collaborators[]-&amp;gt;[role == 'instructor'][0]{
          title,
          'slug': person-&amp;gt;slug.current,
          'name': person-&amp;gt;name,
          'path': person-&amp;gt;website,
          'twitter': person-&amp;gt;twitter,
          'image': person-&amp;gt;image.url
        },
        'background': images[label == 'background'][0].url,
        'image': images[label == 'badge'][0].url,
      }
  `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;s-maxage=1, stale-while-revalidate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is getting better. It produces the same data structure, and we can now live-update the header component inside of Sanity Studio and don't need to redeploy. While that is creating documents that are tied to specific pages, it isn't modeling data for layout and gives us a ton of flexibility.&lt;/p&gt;



&lt;p&gt;If this is your first time seeing GROQ it might look strange, but it's actually a fascinating and relatively simple way to query data. Here's the &lt;a href="https://www.sanity.io/docs/query-cheat-sheet"&gt;official GROQ Cheat Sheet&lt;/a&gt; that gives a great overview.&lt;/p&gt;



&lt;p&gt;Taking that a step further I can consider replacing the entire &lt;code&gt;home-page-date.ts&lt;/code&gt; with a loader that looks like this using what is referred to as an "outer reflection" in Sanity's GROQ query language:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sanityClient&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utils/sanity-client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;groq&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;groq&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;loadHomePageData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slugs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;jumbotron&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home-page-jumbotron&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sanityClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;groq&lt;/span&gt;&lt;span class="s2"&gt;`
      {
        'jumbotron': &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;jumbotronQuery&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,
      }
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;slugs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jumbotronQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;groq&lt;/span&gt;&lt;span class="s2"&gt;`
*[slug.current == $jumbotron][0]{
  name,
  title,
  description,
  summary,
  byline, 
  meta,
  path,
  'slug': resources[][0]-&amp;gt;_id,
  'instructor': collaborators[]-&amp;gt;[role == 'instructor'][0]{
    title,
    'slug': person-&amp;gt;slug.current,
    'name': person-&amp;gt;name,
    'path': person-&amp;gt;website,
    'twitter': person-&amp;gt;twitter,
    'image': person-&amp;gt;image.url
  },
  'background': images[label == 'background'][0].url,
  'image': images[label == 'badge'][0].url,
}
`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach would allow me to add a structured query to load the data for each section, feature, and call to action (CTA) on the home page of the site and give the team the ability to update, curate, and tend our collaborative digital community garden without requiring a deploy of the front end.&lt;/p&gt;

&lt;p&gt;If we want to change the design or switch out the data we are loading, that starts to become simpler as well.&lt;/p&gt;

&lt;p&gt;We are able to layer assets on top of our resources with ease and allow our designers and editorial team members to have more collaborative creative control over the resulting page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we are headed...
&lt;/h2&gt;

&lt;p&gt;This is a great start. Sanity has a lot of potential and we haven't even begun to scratch the surface. We are going to be tuning our content model and importing more data first. Then we will start to explore Sanity's Structure Builder, which can be used to design content pipelines and workflows that give us even more flexibility in our digital gardening process.&lt;/p&gt;

&lt;p&gt;Having a bespoke, contextual, lovely content authoring tool at our disposal is exciting, and I'm looking forward to digging in more.&lt;/p&gt;

&lt;p&gt;Questions? Please feel free to &lt;a href="https://twitter.com/jhooks"&gt;ask on twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>sanity</category>
      <category>data</category>
      <category>react</category>
    </item>
    <item>
      <title>TailwindCSS Dark Mode in Next.js with Tailwind Typography Prose Classes</title>
      <dc:creator>Joel Hooks 🌩</dc:creator>
      <pubDate>Wed, 27 Jan 2021 02:16:37 +0000</pubDate>
      <link>https://dev.to/egghead/tailwindcss-dark-mode-in-next-js-with-tailwind-typography-prose-classes-59l4</link>
      <guid>https://dev.to/egghead/tailwindcss-dark-mode-in-next-js-with-tailwind-typography-prose-classes-59l4</guid>
      <description>&lt;p&gt;When you release a modern website one thing is clear... users expect dark mode out of the box. They aren't interested in your excuses. They don't care about the time it will take to implement, they just want dark mode. Now. 😅&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will learn about in this article.
&lt;/h2&gt;

&lt;p&gt;This article is going to explain in clear steps how to add TailwindCSS native&lt;br&gt;
dark mode to a Next.js site, including the TailwindCSS Typography plugins &lt;code&gt;prose&lt;/code&gt;&lt;br&gt;
classes.&lt;/p&gt;

&lt;p&gt;There is an assumption that you have a working knowledge of both TailwindCSS and&lt;br&gt;
Next.js and a site that you'd like to implement a toggle between a &lt;code&gt;dark&lt;/code&gt; and a&lt;br&gt;
&lt;code&gt;light&lt;/code&gt; theme.&lt;/p&gt;

&lt;p&gt;To do this, you will use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js: A React "meta-framework"&lt;/li&gt;
&lt;li&gt;TailwindCSS: A utility-class system for styling web applications&lt;/li&gt;
&lt;li&gt;TailwindCSS Typography: A plugin that provides a set of &lt;code&gt;prose&lt;/code&gt; classes that provide
consistently nice looking typographic defaults (useful for Markdown files, for instance)&lt;/li&gt;
&lt;li&gt;next-themes: React Hooks based utility library for Next.js that let's you switch themes
in your application.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Motivation for dark mode
&lt;/h2&gt;

&lt;p&gt;With a recent relaunch of egghead.io there were daily requests for a "dark mode" for&lt;br&gt;
the website. In the past our site had had a default singular dark theme, meaning a theme where the background is dark, and the text is light. The new site presented a solid&lt;br&gt;
white–incredibly bright–theme that wasn't very pleasant for many users viewing experience.&lt;/p&gt;

&lt;p&gt;Bright themes are particularly aggravating when you are working in a dark room, and&lt;br&gt;
some users have vision troubles that are exacerbated by light or dark themes. This&lt;br&gt;
means that the ability to choose between one or the other is often critical for some&lt;br&gt;
users' ability to use the site at all.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you don't have a Next.js + TailwindCSS site to work from, &lt;a href="https://github.com/joelhooks/next-typescript-tailwind-mdx-starter/tree/1-pre-dark-mode"&gt;here's a github branch&lt;br&gt;
from my Next.js Tailwind Starter&lt;/a&gt; that is "pre-dark mode" that you can use.&lt;/p&gt;

&lt;p&gt;From this point we need to update some configuration files.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Tailwind Config
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;tailwind.config.js&lt;/code&gt; is in the root directory of the project and provides TailwindCSS&lt;br&gt;
the information it needs to correctly run in your environment. The TailwindCSS team&lt;br&gt;
has done a great job giving us sensible defaults, but almost every project will have&lt;br&gt;
specific needs and requirements that require custom configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({}),&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tailwindcss/typography&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This config is almost as basic as it can be. Since you are using the TailwindCSS Typography plugin, this config let's TailwindCSS know that you want to use it. The config also has a &lt;code&gt;purge&lt;/code&gt; property that provides an array o &lt;a href="https://www.man7.org/linux/man-pages/man7/glob.7.html"&gt;globs&lt;/a&gt; letting TailwindCSS know which files it should analyze for purging extra classes not used in your application. If we didn't configure purging, the result would be &lt;em&gt;every single class TailwindCSS has to offer&lt;/em&gt; being shipped with our application.&lt;/p&gt;

&lt;p&gt;That might not be the end of the world, but it is a &lt;strong&gt;lot&lt;/strong&gt; of extra bundle size that your users will never actually need.&lt;/p&gt;

&lt;p&gt;So we purge.&lt;/p&gt;

&lt;p&gt;After the purge configuration the see the &lt;code&gt;theme&lt;/code&gt;, &lt;code&gt;variants&lt;/code&gt;, and &lt;code&gt;plugins&lt;/code&gt;. Right now these sections are sparse, but that's about to change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling Dark Mode in TailwindCSS
&lt;/h2&gt;

&lt;p&gt;Enabling dark mode in TailwindCSS is effectively the flip of a switch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({}),&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tailwindcss/typography&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding &lt;code&gt;darkmode: 'class'&lt;/code&gt; to the config, you've instructed TailwindCSS to include all of the CSS utility classes for dark mode. This enables a &lt;code&gt;dark&lt;/code&gt; variant that you can now add as classes to your React elements like &lt;code&gt;className="bg-white dark:bg-gray-900"&lt;/code&gt; and the correct class will be provided when &lt;code&gt;dark&lt;/code&gt; is active on your &lt;code&gt;html&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;To test out dark mode in the Next.js app, you'll need to make a couple of changes to the &lt;code&gt;/src/_document.tsx&lt;/code&gt; source file that is used to provide custom document structure to the Next.js application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Html&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dark"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dark:bg-gray-800"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NextScript&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we add the &lt;code&gt;dark&lt;/code&gt; class to the &lt;code&gt;Html&lt;/code&gt; element. This enables the dark mode for the entire application. Then we add &lt;code&gt;dark:bg-gray-800&lt;/code&gt; to the &lt;code&gt;body&lt;/code&gt; element to provide a dark background for the Next'js application when it is in dark mode.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn dev&lt;/code&gt; will run the application, and you should see a dark background. Delete &lt;code&gt;dark&lt;/code&gt; from the &lt;code&gt;Html&lt;/code&gt; elements &lt;code&gt;className&lt;/code&gt; and your app should refresh with a default white background.&lt;/p&gt;

&lt;p&gt;We've achieved dark mode! 🌑&lt;/p&gt;

&lt;p&gt;Obviously your users aren't going to change source code to enabled toggling, so the next step is to add a button that will toggle the dark mode on and off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a theme with next-themes and React Hooks
&lt;/h2&gt;

&lt;p&gt;Technically your app is going to have two themes: &lt;code&gt;light&lt;/code&gt; and &lt;code&gt;dark&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Potentially your app could have many themes up to and including &lt;a href="https://blog.codinghorror.com/a-tribute-to-the-windows-31-hot-dog-stand-color-scheme/"&gt;hot dog stand&lt;/a&gt;. That's amazing if you want to provide your users with that level of flexibility! lol&lt;/p&gt;

&lt;p&gt;There are several relatively complicated ways you might approach the problem of toggling themes. As with many things in the React.js and Next.js world, somebody else has already solved the problem very well, and for this the community favorite is &lt;a href="https://github.com/pacocoursey/next-themes"&gt;next-themes&lt;/a&gt; which promises (and subsequently delivers) a "perfect dark mode in two lines of code".&lt;/p&gt;

&lt;p&gt;Yes please.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yarn add next-themes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;/src/_app.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;AppProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DefaultSeo&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;SEO&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in &lt;code&gt;/src/_app.js&lt;/code&gt; import the &lt;code&gt;ThemeProvider&lt;/code&gt; and wrap your application &lt;code&gt;Component&lt;/code&gt; with it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-themes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;AppProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DefaultSeo&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;SEO&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, nothing has really changed in the app. Since &lt;code&gt;dark&lt;/code&gt; is hard-coded in your &lt;code&gt;_app.tsx&lt;/code&gt; and there is no mechanism to toggle the mode, your application is stuck in dark mode.&lt;/p&gt;

&lt;p&gt;Go ahead and delete the &lt;code&gt;className&lt;/code&gt; from the &lt;code&gt;Html&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dark:bg-gray-800"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Main&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NextScript&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your application will reload, and will once again have the default white background that got us into this situation in the first place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Toggling between light and dark modes with just a click
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;/src/pages/index.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl text-pink-500"&lt;/span&gt; &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;teal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Welcome to Your App
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is relatively simple React page component that is located at the root of the site. It defines a &lt;code&gt;div&lt;/code&gt; as a container and an &lt;code&gt;h1&lt;/code&gt; element with a bit of welcome text and some questionably stylish classes applied.&lt;/p&gt;

&lt;p&gt;To make the toggle work, we need to import a hook from &lt;code&gt;next-themes&lt;/code&gt;, manage a little piece of state, and wire it all together in a button.&lt;/p&gt;

&lt;p&gt;First, import the &lt;code&gt;useTheme&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-themes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl text-pink-500"&lt;/span&gt; &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;teal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Welcome to Your App
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now call the &lt;code&gt;useTheme&lt;/code&gt; hook to gain access to &lt;code&gt;theme&lt;/code&gt; and &lt;code&gt;setTheme&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-themes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl text-pink-500"&lt;/span&gt; &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;teal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Welcome to Your App
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, add a &lt;code&gt;button&lt;/code&gt; element with an &lt;code&gt;onClick&lt;/code&gt; handler to use as a toggle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-themes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl text-pink-500"&lt;/span&gt; &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;teal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Welcome to Your App
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;toggle&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To toggle, we want to check and see what the current theme is, and set the appropriate theme based on that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-themes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useTheme&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-3xl text-pink-500"&lt;/span&gt; &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;backgroundColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;teal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Welcome to Your App
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        toggle
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A couple of things to note are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The button is completely unstyled and doesn't really look like a button&lt;/li&gt;
&lt;li&gt;clicking it does absolutely nothing 😭&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first issue just means you need to use tailwind to make the button look awesome, but the second issue is more concerning and you need to address that to get this toggle working at all. It's a multi-faceted problem resulting from how we've configured dark mode.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;tailwind.config.js&lt;/code&gt; you set &lt;code&gt;darkMode&lt;/code&gt; with the &lt;code&gt;class&lt;/code&gt; option. This configuration property also has a &lt;code&gt;media&lt;/code&gt; option that uses the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media of modern browsers and operating systems to look at how the user has configured their system. The &lt;code&gt;class&lt;/code&gt; option, however, means we can select and toggle the mode. In fact, you could delete the &lt;code&gt;button&lt;/code&gt;, set the &lt;code&gt;darkMode&lt;/code&gt; config to &lt;code&gt;media&lt;/code&gt; and call it a day.&lt;/p&gt;

&lt;p&gt;For many use cases the &lt;code&gt;class&lt;/code&gt; config is the most flexible and is preferred.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;/src/_app.js&lt;/code&gt; you need to tell the &lt;code&gt;ThemeProvider&lt;/code&gt; to use the class attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt; &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ThemeProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let your app compile, refresh the page, and start toggling. Back and forth. Dazzling. A &lt;strong&gt;fully configured dark mode powered by Tailwind CSS in a Next.js app&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The future is now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving some problems with our TailwindCSS config and dark mode
&lt;/h2&gt;

&lt;p&gt;This is great. It works!&lt;/p&gt;

&lt;p&gt;There are still a couple of problems to solve:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build times are slooooooow (on large projects they can also completely run out of memory)&lt;/li&gt;
&lt;li&gt;If you visit &lt;code&gt;/hi&lt;/code&gt; - an mdx file rendered and presented with TailwindCSS Typography &lt;code&gt;prose&lt;/code&gt; class, you notice that the text is black.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Slow builds with TailwindCSS Dark Mode and Next.js
&lt;/h3&gt;

&lt;p&gt;This is a known problem that is at the core a webpack issue and both the Next.js team and the TailwindCSS team are aware of it. Basically, TailwindCSS + Dark Mode is a &lt;strong&gt;massive&lt;/strong&gt; CSS file, and webpack hates building source maps for massive CSS files.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  👋 If you know how to solve this please hit me up on{' '}&lt;br&gt;
  &lt;a href="https://twitter.com/jhooks"&gt;Twitter&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;For our application this is a huge hassle and requires that we run the development environment with additional memory allocated to node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NODE_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;--max-old-space-size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4048 yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ultimately it's a small price to pay for dark mode, and will eventually be fixed upstream. It was also alleviated a bit for us by turning on purging for the dev environment in &lt;code&gt;tailwind.config.css&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({}),&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tailwindcss/typography&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These options require &lt;code&gt;purge&lt;/code&gt; to be an object instead of an array. We set &lt;code&gt;enabled: true&lt;/code&gt; and &lt;code&gt;content: ['./src/**/*.tsx']&lt;/code&gt; which is the same array as we had previously set &lt;code&gt;purge&lt;/code&gt; to.&lt;/p&gt;

&lt;p&gt;Purging CSS means that TailwindCSS tries it best to analyze the source that you've pointed to in &lt;code&gt;content&lt;/code&gt; and not remove any CSS classes that you've used.&lt;/p&gt;

&lt;p&gt;You can test it now by running the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn build
yarn start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Controlling the Purge
&lt;/h3&gt;

&lt;p&gt;If all is well, your app should function as expected. If toggling dark mode doesn't work or appear to do anything, it could mean that the &lt;code&gt;dark&lt;/code&gt; CSS class variants have been stripped from your application because the &lt;code&gt;dark&lt;/code&gt; class isn't assigned to a &lt;code&gt;className&lt;/code&gt; by default.&lt;/p&gt;

&lt;p&gt;In this example, that doesn't appear to be happened, but if you encounter this in your application where it works in development, but not in production you might need to add a &lt;code&gt;safelist&lt;/code&gt; property to your &lt;code&gt;tailwind.config.js&lt;/code&gt; purge options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;safelist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;//specific classes&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({}),&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tailwindcss/typography&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;safelist&lt;/code&gt; allows you to specify classes that TailwindCSS will always keep around for you and not purge. At the time of this writing the only documentation for this is &lt;a href="https://github.com/tailwindlabs/tailwindcss/discussions/2793#discussioncomment-142172"&gt;buried in some Github issue comments&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dark Mode for TailwindCSS Typography Prose Classes
&lt;/h2&gt;

&lt;p&gt;By default TailwindCSS Typography doesn't support dark mode. Prose classes are also notoriously challenging to customize. You can't just set a &lt;code&gt;className&lt;/code&gt; instead you need to override defaults in your &lt;code&gt;tailwind.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;theme&lt;/code&gt; section of the config you a &lt;code&gt;typography&lt;/code&gt; property under &lt;code&gt;extend&lt;/code&gt; which allows us to &lt;strong&gt;extend&lt;/strong&gt; the &lt;code&gt;@tailwindcss/typography&lt;/code&gt; plugin. The configuration property takes a function that passes in the &lt;code&gt;theme&lt;/code&gt; and returns an object that extends the theme for that plugin.&lt;/p&gt;

&lt;p&gt;It makes me a little dizzy to think about, but the extension we return adds a &lt;code&gt;dark&lt;/code&gt; property with a &lt;code&gt;css&lt;/code&gt; property that sets &lt;code&gt;color: 'white'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, in &lt;code&gt;/src/layouts/index.tsx&lt;/code&gt; on line 28 you'll find the &lt;code&gt;prose&lt;/code&gt; class being applied to a &lt;code&gt;div&lt;/code&gt;. This file is the default layout that &lt;code&gt;mdx&lt;/code&gt; files use in your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"prose md:prose-xl max-w-screen-md"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl leading-tight"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add &lt;code&gt;dark:prose-dark&lt;/code&gt; and &lt;code&gt;dark:md:prose-xl-dark&lt;/code&gt; to the &lt;code&gt;className&lt;/code&gt; of the &lt;code&gt;div&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"prose md:prose-xl dark:prose-dark dark:md:prose-xl-dark"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl leading-tight"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refresh...&lt;/p&gt;

&lt;p&gt;Nothing happens. No changes. There's another step in the &lt;code&gt;tailwind.config.js&lt;/code&gt; in the &lt;code&gt;variants&lt;/code&gt; config add &lt;code&gt;typography: ['dark']&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/**/*.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;safelist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;//specific classes&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({}),&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;dark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tailwindcss/typography&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voíla! You should see the body text of &lt;code&gt;http://localhost:3000/hi&lt;/code&gt; become &lt;code&gt;white&lt;/code&gt; as configured.&lt;/p&gt;

&lt;p&gt;There are a &lt;strong&gt;lot&lt;/strong&gt; of options for customizing the look and feel of your markdown. If you want some inspiration, Lee Rob has done a wonderful job for his personal site and you can &lt;a href="https://github.com/leerob/leerob.io/blob/main/tailwind.config.js#L33-L72"&gt;check out the config here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Users want dark mode and to set it up with TailwindCSS and Next.js it requires some configuration and basic state management. What you've done so far is just a start, and there is a lot of room to expand on the styles to make your application shine.&lt;/p&gt;

&lt;p&gt;If you'd like to look more closely at a larger scale full-featured application (the one you are looking at right now in fact), you can &lt;a href="https://github.com/eggheadio/egghead-next"&gt;checkout the repository for the egghead website on Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's the &lt;a href="https://github.com/joelhooks/next-typescript-tailwind-mdx-starter/tree/2-hello-darkness"&gt;end state of the project you've been working on in this article on Github as well&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you've got any questions, please &lt;a href="https://twitter.com/jhooks"&gt;ask them on Twitter&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;There's also an edit link below if you'd like to send any corrections or updates directly ⭐️&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Illustrated Notes on Web Security Essentials</title>
      <dc:creator>Maggie Appleton</dc:creator>
      <pubDate>Mon, 30 Nov 2020 18:03:02 +0000</pubDate>
      <link>https://dev.to/egghead/illustrated-notes-on-web-security-essentials-3ob7</link>
      <guid>https://dev.to/egghead/illustrated-notes-on-web-security-essentials-3ob7</guid>
      <description>&lt;p&gt;Between IoT botnet attacks, Bitcoin ransomware, and the weekly cadence of high-profile data breaches, doing anything on the internet feels like playing Russian Roulette.&lt;/p&gt;

&lt;p&gt;And that's just for normal people using it.&lt;/p&gt;

&lt;p&gt;As a developer, you're partially responsible for protecting all those normal people from the horrors of whatever LulzSec are up to right now. The least you can do is make sure your website isn't an accessory to their nefarious activities.&lt;/p&gt;

&lt;p&gt;So what does the current landscape of internet risk looks like. How, exactly, is LulzSec going to hack into my mainframe in 2020?&lt;/p&gt;

&lt;p&gt;Here's my illustrated notes from Mike Sherov's egghead course on '&lt;a href="https://bit.ly/39tzKth" rel="noopener noreferrer"&gt;Web Security Essentials&lt;/a&gt;' that should give you a big picture overview of what to should look out for.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcpvahacov3gsz0hnaxpm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcpvahacov3gsz0hnaxpm.png" alt="web-security-1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Turns out there's a small set of fairly well-known and easy to defend attacks that we can protect ourselves from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Man in the Middle (MITM)&lt;/li&gt;
&lt;li&gt;Cross-Site Request Forgery (CSRF)&lt;/li&gt;
&lt;li&gt;Cross-Site Scripting (XSS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shielding yourself from these three is a great starting point. It's at least enough to keep the script kiddies out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwtiuqo47g17t3qzwjfqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwtiuqo47g17t3qzwjfqp.png" alt="web-security-2"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fravw48zq7vq5r9wsrv6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fravw48zq7vq5r9wsrv6h.png" alt="web-security-6"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsn3clruyzcohbzie3z68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsn3clruyzcohbzie3z68.png" alt="web-security-7"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwya3k4ucpcesp5s16ihm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwya3k4ucpcesp5s16ihm.png" alt="web-security-3"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo6df0lif283qvoc7n5wz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo6df0lif283qvoc7n5wz.png" alt="web-security-4"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzordhyjxh7fzmpb27ifj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzordhyjxh7fzmpb27ifj.png" alt="web-security-5"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Keeping out this trio of attacks will go a long way. Thankfully, you do not have to brave the black hat darkness alone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bit.ly/39tzKth" rel="noopener noreferrer"&gt;Mike Sherov's course&lt;/a&gt; covers a whole range of ways to mitigate your risk and protect against them. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5oir528fi51jqdwkqzna.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5oir528fi51jqdwkqzna.png" alt="web-security-essentials-mitm-csrf-and-xss"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;👁‍🗨 If these are hard to read, you can download a free high-res PDF version on the course itself.&lt;br&gt;
Dev.to limits image sizes a lot and it's hard to make it readable on here. Apologies!&lt;/p&gt;

</description>
      <category>security</category>
      <category>infosec</category>
    </item>
    <item>
      <title>WTF is Rust? The Illustrated Notes</title>
      <dc:creator>Maggie Appleton</dc:creator>
      <pubDate>Mon, 23 Nov 2020 16:46:55 +0000</pubDate>
      <link>https://dev.to/egghead/wtf-is-rust-the-illustrated-notes-564p</link>
      <guid>https://dev.to/egghead/wtf-is-rust-the-illustrated-notes-564p</guid>
      <description>&lt;p&gt;The &lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;Rust programming language&lt;/a&gt; keeps winning the middle-school popularity contest of the developer world: "most loved" on &lt;a href="https://insights.stackoverflow.com/survey/2019#technology" rel="noopener noreferrer"&gt;Stack Overflow surveys&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6wz9nsscvjx9e70np2n0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6wz9nsscvjx9e70np2n0.png" alt="stackoverflow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, what's this Rust thing and why is everyone enamoured?&lt;/p&gt;

&lt;p&gt;Let's explore...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiz58kqzpxuwqw2p8q9ra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiz58kqzpxuwqw2p8q9ra.png" alt="WTFRust_1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvtal437vsru30jdb2k7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvtal437vsru30jdb2k7l.png" alt="WTFRust_2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flsj1xf0v7owkqzb7tkoz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flsj1xf0v7owkqzb7tkoz.png" alt="WTFRust_4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This happy balance Rust manages to strike – between speed, control over memory, and human-friendliness is what makes it so popular.&lt;/p&gt;

&lt;p&gt;The kind of programmes you can write with Rust would otherwise be made in a language like C or C++. A lot of developers find the C-suite challenging to work with, so this friendly alternative is very welcome!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's got the Goldilocks Effect going on.   Not too slow, not too dangerous, not too complicated – just right.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;The Rust community is also infamous for being welcoming, open, and encouaging to newcomers. Fostering that kind of culture is very likely why it's gained so much traction in the last few years.&lt;/p&gt;

&lt;p&gt;Anyone who hangs out in Developer World long enough realises frameworks and languages are identities much more than they're tools. You belong to Team React, or Camp Python, or the Rust Crew.&lt;/p&gt;

&lt;p&gt;And no one wants to join a bunch of judgey gatekeepers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd49przduerjok68p26s5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd49przduerjok68p26s5.png" alt="WTFRust_5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnc7671f66sp5cbkea9ux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnc7671f66sp5cbkea9ux.png" alt="WTFRust_3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fenlx0cemgo2afip3sadp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fenlx0cemgo2afip3sadp.png" alt="WTFRust_6"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;I've just done a sweeping and completely incomplete overview of Rust here. But it's enough to point you in the right direction.&lt;/p&gt;

&lt;p&gt;While researching and making these I watched &lt;a href="https://twitter.com/PascalPrecht" rel="noopener noreferrer"&gt;Pascal Precht&lt;/a&gt;'s egghead course that shows you how to &lt;a href="http://bit.ly/39EPEyl" rel="noopener noreferrer"&gt;Write Your First Rust Programme&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flcnx9guhiegsu998ikz5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flcnx9guhiegsu998ikz5.png" alt="write-your-first-program-with-the-rust-language"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;It's free 20 minute course that shows you all the basics.&lt;/p&gt;




&lt;p&gt;👁‍🗨 If these are hard to read, you can download a free high-res PDF version &lt;a href="http://bit.ly/39EPEyl" rel="noopener noreferrer"&gt;on the course itself&lt;/a&gt;.&lt;br&gt;
Dev.to limits image sizes a lot and it's hard to make it readable on here. Apologies!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>illustrated</category>
    </item>
    <item>
      <title>Illustrated Notes on Custom React Hooks</title>
      <dc:creator>Maggie Appleton</dc:creator>
      <pubDate>Fri, 20 Nov 2020 10:21:09 +0000</pubDate>
      <link>https://dev.to/egghead/illustrated-notes-on-custom-react-hooks-5b4a</link>
      <guid>https://dev.to/egghead/illustrated-notes-on-custom-react-hooks-5b4a</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk3hxrcz468zgpl7r2hj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk3hxrcz468zgpl7r2hj7.png" alt="CustomHooks_DevTo_1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Illustrated notes on &lt;a href="https://joeprevite.com/" rel="noopener noreferrer"&gt;Joe Previtte&lt;/a&gt;'s course on &lt;a href="http://bit.ly/3av6FvK" rel="noopener noreferrer"&gt;Building Custom React Hooks&lt;/a&gt; on egghead&lt;/p&gt;




&lt;h2&gt;
  
  
  The Basics of React Hooks
&lt;/h2&gt;

&lt;p&gt;If you're not up to speed on &lt;a href="https://reactjs.org/docs/hooks-intro.html" rel="noopener noreferrer"&gt;React hooks&lt;/a&gt;, here's a very quick overview...&lt;/p&gt;

&lt;p&gt;Hooks let us do more than just display static UI components. They give us the ability to hold state in our components, and use lifecycle events that can handle side effects and data changing over time.&lt;/p&gt;

&lt;p&gt;We used to do this with &lt;a href="//16.8"&gt;class components&lt;/a&gt;, but the React library added hooks in early 2019 with version 16.8.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu637kljxekttuoed30dq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu637kljxekttuoed30dq.png" alt="CustomHooks_DevTo_2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuz0vu063711wgpzpffne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuz0vu063711wgpzpffne.png" alt="CustomHooks_DevTo_3"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdghhearnqki9yu18gukg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdghhearnqki9yu18gukg.png" alt="CustomHooks_DevTo_4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Two Main Hooks
&lt;/h2&gt;

&lt;p&gt;The React library comes with a set of "build in" hooks. There are &lt;a href="https://reactjs.org/docs/hooks-reference.html" rel="noopener noreferrer"&gt;10 official hooks&lt;/a&gt;, but the two most common ones are &lt;strong&gt;useState&lt;/strong&gt; and &lt;strong&gt;useEffect&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. useState
&lt;/h3&gt;

&lt;p&gt;useState lets us hold "state" – a piece of data that changes over time while your app is up and running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1cg6qoks3qe0h8godqp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1cg6qoks3qe0h8godqp9.png" alt="CustomHooks_DevTo_5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. useEffect
&lt;/h3&gt;

&lt;p&gt;useEffect let's us perform side effects. That means we can execute functions or run code that isn't directly about rendering UI elements to the DOM.&lt;/p&gt;

&lt;p&gt;This includes making data requests, or running JavaScript functions &lt;em&gt;after&lt;/em&gt; the components have already rendered.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbwmt1hiwak5zjf0g3r0e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbwmt1hiwak5zjf0g3r0e.png" alt="CustomHooks_DevTo_6"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fps7c22vykz9vx7rri0zh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fps7c22vykz9vx7rri0zh.png" alt="CustomHooks_DevTo_7"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Creating Your Own Custom Hooks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6nbj4uaeodmb9hjrpty8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6nbj4uaeodmb9hjrpty8.png" alt="CustomHooks_DevTo_8"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also remix these built-in hooks to create &lt;strong&gt;custom hooks&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;These are simply functions you write that use the basic hooks, and layer extra functionality on top of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiukemhqs64t9inmw11iw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiukemhqs64t9inmw11iw.png" alt="CustomHooks_DevTo_9"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fql2g96fk789kp79a3ul2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fql2g96fk789kp79a3ul2.png" alt="CustomHooks_DevTo_10"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a great collection on &lt;a href="https://usehooks.com/" rel="noopener noreferrer"&gt;usehooks.com&lt;/a&gt; if you want to see some examples.&lt;/p&gt;

&lt;p&gt;Once you've written your hook, there's a handy &lt;a href="https://www.npmjs.com/package/create-react-hook" rel="noopener noreferrer"&gt;create-react-hook&lt;/a&gt; library that does all the formatting grunt work and makes uploading your hook to npm easy.&lt;/p&gt;

&lt;p&gt;Just use &lt;code&gt;npx create-react-hook&lt;/code&gt;, add a package name, description, Github details, and licence. There's a default template that works for most cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkw9i35z619p9b48aphu8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkw9i35z619p9b48aphu8.png" alt="CustomHooks_DevTo_11"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It'll generate a series of files and folders for you. You just need to add your hook file to the &lt;code&gt;/src&lt;/code&gt; folder, write the readme, and take care of some other housekeeping.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8xt2nb36nxtsj2h0a58b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8xt2nb36nxtsj2h0a58b.png" alt="CustomHooks_DevTo_12"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that you're all ready to publish your hook for others to use 🎉&lt;/p&gt;




&lt;p&gt;&lt;a href="http://bit.ly/3av6FvK" rel="noopener noreferrer"&gt;Joe's course&lt;/a&gt; is nice and short, and covers all the details of how to do this.&lt;/p&gt;

&lt;p&gt;You can get a handle on custom hooks in under 30 minutes. Then start writing your own!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3o7ynn3shq7egu3gpe9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3o7ynn3shq7egu3gpe9x.png" alt="shareable-custom-hooks-in-react"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;👁‍🗨 If these are hard to read, you can download a free high-res PDF version &lt;a href="http://bit.ly/3av6FvK" rel="noopener noreferrer"&gt;on the course itself&lt;/a&gt;.&lt;br&gt;
Dev.to limits image sizes a lot and it's hard to make it readable on here. Apologies!&lt;/p&gt;

</description>
      <category>react</category>
      <category>hooks</category>
      <category>javascript</category>
      <category>illustrated</category>
    </item>
    <item>
      <title>Illustrated Notes on Fixing Git Mistakes</title>
      <dc:creator>Maggie Appleton</dc:creator>
      <pubDate>Tue, 17 Nov 2020 17:39:55 +0000</pubDate>
      <link>https://dev.to/egghead/illustrated-notes-on-fixing-git-mistakes-1c16</link>
      <guid>https://dev.to/egghead/illustrated-notes-on-fixing-git-mistakes-1c16</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftsbsimsb1nxz134m5yo0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftsbsimsb1nxz134m5yo0.png" alt="FixGitMistakes_4_DevTo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are illustrated notes I made while working through &lt;a href="https://chrisachard.com/" rel="noopener noreferrer"&gt;Chris Achard&lt;/a&gt;'s &lt;a href="http://bit.ly/gitfix" rel="noopener noreferrer"&gt;Fix Common Git Mistakes&lt;/a&gt; course on egghead.&lt;/p&gt;

&lt;p&gt;The course is a great refresher on the fundamental structure of git, adding and removing commits, and (critically) undoing mistakes of all kinds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fle6fafirpbq05vyv6xtb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fle6fafirpbq05vyv6xtb.png" alt="fix-common-git-mistakes title"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Basic Structure of Git
&lt;/h2&gt;

&lt;p&gt;Chris covers all the various 'levels' your Git files can be at. I personally needed to think of it as a spatial stack to understand how files move between them.&lt;/p&gt;

&lt;p&gt;Every time I push commits to Github I see something like this in my head:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2wkf22g4qncsurrpypzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2wkf22g4qncsurrpypzd.png" alt="FixGitMistakes_3_DevTo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also found it helpful to think of the 'stash' as a side drawer you tuck files away in, and then can 'pop' them back out later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Branches are Pointers
&lt;/h2&gt;

&lt;p&gt;This framing of git branches as "pointers" you can move around changed the way I think about them a lot.&lt;/p&gt;

&lt;p&gt;We can move the pointers to different commits, which are just different versions of our project at a specific point in time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fadrtw3t9ima7vimsbay6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fadrtw3t9ima7vimsbay6.png" alt="FixGitMistakes_1_DevTo"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Git Log
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git log&lt;/code&gt; is a handy feature that shows you all your previous commits and their details. It's quite verbose though.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git log --oneline&lt;/code&gt; is much easier to read since it only shows you the commit hashes and messages&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git log --graph&lt;/code&gt; draws a tiny graph in the terminal showing branches and merges&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fj3syvt51em8xuuoc4cvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fj3syvt51em8xuuoc4cvu.png" alt="Artboard 2_DevTo"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Undoing Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbtf0y16b5hzjcx1k4bmu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbtf0y16b5hzjcx1k4bmu.png" alt="Artboard 4_DevTo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Commit Ammend
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git commit --ammend&lt;/code&gt; lets us add or change files in our last commit, as well as the commit message&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcke5jpmnb823wl6o6x2a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcke5jpmnb823wl6o6x2a.png" alt="Artboard 3_DevTo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Reset
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git reset&lt;/code&gt; will move a file backwards. If you're committed a file to either staging or the local repo, we can bring it back to our working directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fx32i3x8nu8ki07rsgljt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fx32i3x8nu8ki07rsgljt.png" alt="Artboard 5_DevTo"&gt;&lt;/a&gt;&lt;br&gt;
Git gives us three levels of "intensity" for resetting - &lt;code&gt;git reset --hard&lt;/code&gt;, &lt;code&gt;git reset --soft&lt;/code&gt;, and mixed.&lt;/p&gt;

&lt;p&gt;If you don't specify one, mixed is the default which just moves it from the local repo back to your working files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7245xfgzdsvtxhjob2ed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7245xfgzdsvtxhjob2ed.png" alt="Artboard 6_DevTo"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Git Diff
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;git diff&lt;/code&gt; is a handy utility for seeing what's changed between two commits or two files&lt;/p&gt;

&lt;p&gt;You can compare two commits using their branch names or commit hashes: &lt;code&gt;git diff main new-feature&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can also compare two files by passing in both file names: &lt;code&gt;git diff path/to/file/ComponentA.js path/to/file/ComponentB.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fze24zgrcd8vf0ctsp960.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fze24zgrcd8vf0ctsp960.png" alt="FixGitMistakes_2_DevTo"&gt;&lt;/a&gt; &lt;/p&gt;




&lt;h2&gt;
  
  
  Detached Head
&lt;/h2&gt;

&lt;p&gt;A detached head is less morbid than it sounds. It just means we've checked out a commit using it's specific hash name, such as &lt;code&gt;git checkout 49da32&lt;/code&gt;&lt;br&gt;
That commit is now the "head" because it's the most recent version of our project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fy6uv9hczkfjb3egspf2e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fy6uv9hczkfjb3egspf2e.png" alt="Artboard 4 copy1_DevTo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's "detached" because we've navigated there directly and aren't on our usual branch structure. We have to "reattach" the commit back to our branch with &lt;code&gt;git checkout -b my-new-branch-name&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkevqu8alek5itygbiih2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkevqu8alek5itygbiih2.png" alt="Artboard 4 copy2_DevTo"&gt;&lt;/a&gt;&lt;/p&gt;






&lt;p&gt;It feels good to be less lost in the git forest now.&lt;br&gt;
I hope some of these techniques make your git log less sad too :)&lt;/p&gt;

&lt;p&gt;There's plenty more in &lt;a href="http://bit.ly/gitfix" rel="noopener noreferrer"&gt;Chris' course&lt;/a&gt; that I left out here.&lt;/p&gt;




&lt;p&gt;👁‍🗨 If these are hard to read, you can download a free high-res PDF version &lt;a href="http://bit.ly/gitfix" rel="noopener noreferrer"&gt;on the course itself&lt;/a&gt;.&lt;br&gt;
Dev.to limits image sizes a lot and it's hard to make it readable on here. Apologies!&lt;/p&gt;

</description>
      <category>git</category>
      <category>javascript</category>
      <category>fundamentals</category>
    </item>
    <item>
      <title>Illustrated Notes on Advanced JavaScript Fundamentals</title>
      <dc:creator>Maggie Appleton</dc:creator>
      <pubDate>Wed, 04 Nov 2020 19:27:35 +0000</pubDate>
      <link>https://dev.to/egghead/illustrated-notes-on-advanced-javascript-fundamentals-4lbp</link>
      <guid>https://dev.to/egghead/illustrated-notes-on-advanced-javascript-fundamentals-4lbp</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyptnlyus1kk7gwhacraa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyptnlyus1kk7gwhacraa.png" alt="AdvJS_Devto_1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being humans, we rarely learn things linearly. &lt;/p&gt;

&lt;p&gt;No one begins their JavaScript journey by reading the &lt;a href="https://www.ecma-international.org/ecma-262/5.1/" rel="noopener noreferrer"&gt;TC39 language specification&lt;/a&gt; line-by-line like a pedantic masochist.&lt;/p&gt;

&lt;p&gt;Instead we all cobble together a good-enough understanding, leaving holes here and there to fill in later.&lt;/p&gt;

&lt;p&gt;If you're like me, you might have been stumbling through your JavaScript journey without 100% grokking prototypical inheritance, or how call, apply, and bind work on functions.&lt;/p&gt;

&lt;p&gt;It's fine. Stuff mostly works.&lt;/p&gt;

&lt;p&gt;Until it doesn't ¯_(ツ)_/¯&lt;/p&gt;

&lt;p&gt;At some point, it's useful to stop stumbling around and peek back at those holes.&lt;/p&gt;

&lt;p&gt;I found &lt;a href="https://tylerclark.life/" rel="noopener noreferrer"&gt;Tyler Clark&lt;/a&gt;'s egghead course on &lt;a href="http://bit.ly/advanjs" rel="noopener noreferrer"&gt;Advanced JavaScript Foundations&lt;/a&gt; was the perfect opportunity to strengthen my JS foundations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fog-image-egghead-course.now.sh%2Fadvanced-javascript-foundations" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fog-image-egghead-course.now.sh%2Fadvanced-javascript-foundations" alt="Course illustration for advanced js foundations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While working through the course, I illustrated a set of notes to help me understand and remember all the concepts.&lt;/p&gt;




&lt;h2&gt;
  
  
  Primitive Types &amp;amp; Autoboxing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffi23jfu7sk6dg93ih5wn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ffi23jfu7sk6dg93ih5wn.png" alt="AdvJS_Devto_2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiarcq081q6des3f8x1ab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fiarcq081q6des3f8x1ab.png" alt="AdvJS_Devto_3"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Prototypical Inheritence
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fy4qqxeck4g8sslji0i0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fy4qqxeck4g8sslji0i0i.png" alt="AdvJS_Devto_4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F21qod9kkjqmdags9lqkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F21qod9kkjqmdags9lqkg.png" alt="AdvJS_Devto_5"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;New&lt;/code&gt; Keyword
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqwtl1to15pcq3mpnz3a5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqwtl1to15pcq3mpnz3a5.png" alt="AdvJS_Devto_6"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;.this&lt;/code&gt; keyword
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9tukzo8br4ua12493pyp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9tukzo8br4ua12493pyp.png" alt="AdvJS_Devto_7"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F32btc9ychevfztgfvufw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F32btc9ychevfztgfvufw.png" alt="AdvJS_Devto_8"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fou62u9mo4a64m78c9y6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fou62u9mo4a64m78c9y6g.png" alt="AdvJS_Devto_9"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;These illustrations aren't perfectly designed to explain the concepts they're about, and instead work as shorthand notes for me.&lt;/p&gt;

&lt;p&gt;Take a look at &lt;a href="http://bit.ly/advanjs" rel="noopener noreferrer"&gt;Tyler's course&lt;/a&gt; if you want to dig into the concepts more.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>fundamentals</category>
      <category>es6</category>
      <category>illustration</category>
    </item>
    <item>
      <title>Recording a Great Coding Screencast</title>
      <dc:creator>John Lindquist</dc:creator>
      <pubDate>Fri, 23 Oct 2020 22:56:47 +0000</pubDate>
      <link>https://dev.to/egghead/recording-a-great-coding-screencast-g35</link>
      <guid>https://dev.to/egghead/recording-a-great-coding-screencast-g35</guid>
      <description>&lt;p&gt;It seems trivial to record a 1-8 minute screencast, but there are actually quite a few moving parts when it comes to recording a &lt;strong&gt;high quality&lt;/strong&gt; screencast. Here's some of our thoughts on the subject.&lt;/p&gt;

&lt;p&gt;If you're interested in a more in-depth guide to how we create lessons for egghead.io, please check out our &lt;a href="https://howtoegghead.com/instructor" rel="noopener noreferrer"&gt;instructor's guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Screen
&lt;/h2&gt;

&lt;p&gt;First and foremost a coding screencast is about &lt;strong&gt;the code&lt;/strong&gt;, and we need to make sure it looks great. There are a few aspects to this that help ensure that is the case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resolution
&lt;/h3&gt;

&lt;p&gt;1080p is a great target resolution, but we've also had great results when we record at &lt;strong&gt;1280x720(HiDPI)&lt;/strong&gt; mode, giving an effective visible resolution of 1280x720, but &lt;strong&gt;extremely&lt;/strong&gt; crisp. This resolution is achievable on 27" monitors and retina MBPs.&lt;/p&gt;

&lt;p&gt;To enable HiDPI, we use the &lt;a href="https://github.com/avibrazil/RDM" rel="noopener noreferrer"&gt;RDM tool&lt;/a&gt;. On retina MacBooks, this "just works". For external monitors, or non-retina Macs, you can follow &lt;a href="http://cocoamanifest.net/articles/2013/01/turn-on-hidpi-retina-mode-on-an-ordinary-mac.html" rel="noopener noreferrer"&gt;these instructions to enable HiDPI mode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fs3.amazonaws.com%2Ff.cl.ly%2Fitems%2F1b3t3O1p1k3s2w171104%2Fslack-imgs.com.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fs3.amazonaws.com%2Ff.cl.ly%2Fitems%2F1b3t3O1p1k3s2w171104%2Fslack-imgs.com.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using HiDPI mode gives an extremely crisp final product that looks fantastic (readable) on phones and tablets.&lt;/p&gt;

&lt;p&gt;If you can't use this mode, recording at normal 1280x720 is a decent substitute. By constraining to this small window, you are able to fill the screen effectively for coding screencasts.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;

&lt;p&gt;The code is the champion of the screencast. To that end, it deserves &lt;strong&gt;maximum&lt;/strong&gt; horizontal space. It is a good idea to use an 80 or 120 character "column" for the code and bump the font size up to fit.&lt;/p&gt;

&lt;p&gt;We will typically work in a 3 column layout, with the editor taking up 2/3s and a browser on the right side in the remaining 1/3 of the screen. You might prefer to flip back and forth between the browser and the editor, it is up to you.&lt;/p&gt;

&lt;p&gt;It is important to keep in mind that some padding allowances should be considered for the top and bottom of the recording window, as they can get cut off by player chrome.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recording
&lt;/h3&gt;

&lt;p&gt;We strongly encourage recording in small "takes" rather than trying to record the entire lesson. A take can be as small as a single sentence. Try each take as many times as you need. When you make a mistake, just undo your last sentence and try again. Recording in tiny takes sets you up for success when it comes time to edit. &lt;/p&gt;

&lt;p&gt;When it comes to time to actually record your screen, my personal preference is Screenflow. Recording is simple and the editing experience is smooth. But there are plenty of other great options out there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Editing
&lt;/h3&gt;

&lt;p&gt;Now it's time to cut out all the mistakes. If you followed the "take" advice, you can reach for the "ripple delete" tool and easily chop out all of the mistakes you made. I strongly recommend editing your lesson immediately after you record so all the mistakes you made are fresh in your mind.&lt;/p&gt;

&lt;p&gt;The more confident and practice you get with editing, the more confident you will be with making mistakes in your videos. So it's a good idea to try out the whole process a few times when you're just getting started to get the hang of working in a "takes" mindset.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should I Say?
&lt;/h2&gt;

&lt;p&gt;Your words should reflect what's happening on the screen. Don't talk without showing something and don't show something without talking. As you walk through the steps of your lesson, you can keep the user focused with the forward progress you're making. So the more you give instructions like "type this" or "click that", the more the user has to pay attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Volume
&lt;/h3&gt;

&lt;p&gt;Volume is important. &lt;strong&gt;Nothing ruins an awesome screencast quicker than messed up levels&lt;/strong&gt;. I use a small laptop and headphones to test my audio. On the laptop, I want the volume to be clear from across a small room, with minimal distortion. It should be &lt;strong&gt;in stereo&lt;/strong&gt; and sound good in headphones too. With headphones, max volume should be &lt;strong&gt;loud&lt;/strong&gt;, but distortion free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content
&lt;/h2&gt;

&lt;p&gt;This is totally up to you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;What are you stoked about in web development?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;What do you see as being painful, and how do you solve it?&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our basic guideline is to identify pain. A Pain Thesis™, if you will. Identify the pain, state it, and then walk through the solution. There is &lt;strong&gt;so much pain in web development&lt;/strong&gt;, and this provides endless quantities of worthy topics.&lt;/p&gt;

&lt;p&gt;Feel free to scour Stack Overflow for questions and create videos that answer them. Video offers an advantage over a simple copy/pasted code example because you can show the &lt;strong&gt;process&lt;/strong&gt;. Developers &lt;strong&gt;love&lt;/strong&gt; watching process! (And feel free to amaze them with your custom workflows)&lt;/p&gt;

&lt;p&gt;Have fun! Convey the joy you've found in solving software problems and sharing those solutions with others. We're not professors shoving text books down students throats. We're not trying to fill some sort of quota or meet deadlines. We're doing this because we love sharing information and care deeply about code and best practices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Supplemental content
&lt;/h3&gt;

&lt;p&gt;People are very keen on getting their hands on the code. There are a couple approaches to this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Full-on Project: This is how you would normally work. Structured, full project that makes use of dependency management systems and requires a brief README to explain how to get running ASAP.&lt;/li&gt;
&lt;li&gt;  Minimal Files: An HTML file and a JavaScript file. In this case, use CDN links where possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They almost always want code, even for the most trivial things.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stay Focused
&lt;/h3&gt;

&lt;p&gt;If you need to go off on a tangent, consider making another separate video. Your audience is watching your video for what you put in the title, so do your best to limit to that specific topic. This also has the side-benefit of making the video easier to find and organize in groups with other videos.&lt;/p&gt;

&lt;p&gt;We make "bite-sized" videos because our audience's time is precious. Each video should have enough content to satisfy a learning craving while also supporting binge-watching across multiple videos for those developers diving head-first into the latest and greatest technologies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top 10 egghead screencasting tips
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Stick to it. It's worth it. - This is work. You're new to this. Expect it to be hard, but expect to succeed when you put in effort. The final payoff is huge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Accept feedback gracefully.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Relax and have fun. - Humans are more trustworthy than robots. Monotone voices kill lessons. This stuff is interesting, so let it come across in your lessons that you are truly interested.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do Dry Runs - Just hit record and start talking. Create a terrible lesson. You'll learn a ​&lt;em&gt;ton&lt;/em&gt;​ by just doing it live. Then, do it again. You'll nail it down in a few takes and it's much easier than just sitting there stressing about it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;FOCUS - Write your lesson title. Teach only about that title. Don't go off on tangents, instead, MAKE MORE LESSONS!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Show, the flow - Every example has a "stream" of how pieces fit together. It will be obvious to you and you'll be tempted to skip over it because you think it's too obvious. Show how the code goes from point A to point B. Show how task X outputs file Y. Show how button M makes div N disappear.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make A LOT of mistakes, then edit - This is NOT a conference presentation. You do NOT have to be "perfect" or "practiced". You just need to edit out the mistakes. So relax, say stupid stuff, pause, do it again, then edit out the bad stuff later. LOTS of pausing. LOTS of mistakes. Editing is easy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;End fast, start faster - Avoid saying "Hi". Skip "This lesson is about X, Y, Z" Just go. They can read the title of the lesson above the video. Then at the end, just stop. Don't say "Next time", just MAKE THE NEXT LESSON!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Recap when it's complicated - If your lesson has a lot of moving pieces, use the end to walk them through all the steps again. Basically show the flow part 2.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be tidy - If a section gets "sloppy", stop yourself and start over. Having to watch a lesson where an instructor "reacts" the lesson is painful. Take control of the lesson and stay in control. You are the authority.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It takes a little practice, and it can be surprising how long it takes to record a condensed 3 minutes of excellent coding how-to. It definitely gets easier with practice!&lt;/p&gt;

</description>
      <category>screencast</category>
      <category>tutorial</category>
      <category>tips</category>
      <category>guide</category>
    </item>
    <item>
      <title>How xstate saved our 🥓</title>
      <dc:creator>Ian Jones</dc:creator>
      <pubDate>Fri, 23 Oct 2020 15:37:39 +0000</pubDate>
      <link>https://dev.to/egghead/how-xstate-saved-our-3bhg</link>
      <guid>https://dev.to/egghead/how-xstate-saved-our-3bhg</guid>
      <description>&lt;h2&gt;
  
  
  useEffect Overload
&lt;/h2&gt;

&lt;p&gt;egghead is the CMS, sale provider, and authentication for what we call Wes Bos as a Service (WBaaS) sites. This includes TestingJavaScript.com, PureReact.com, and now EpicReact.dev. &lt;/p&gt;

&lt;p&gt;When we set out to build EpicReact.dev, we knew what we were doing. We've built 2 of these sites before and had handled authentication and purchasing. I figured it was time to extract the purchasing code out of these sites into its own package.  &lt;/p&gt;

&lt;p&gt;I used [[tsdx]] because it's a great way to author libraries and who doesn't like some [[TypeScript]]. This process went well. I was enjoying the benefits TypeScript gives you.&lt;/p&gt;

&lt;p&gt;Heres a quick explanation of the commerce package. It was one big &lt;code&gt;useReducer&lt;/code&gt;. I had state that relied on other elements of my state, and &lt;a href="https://kentcdodds.com/blog/should-i-usestate-or-usereducer"&gt;as Kent says&lt;/a&gt;, this is the time to &lt;code&gt;useReducer&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The api of our hook was like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;parityCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;countryName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;onApplyParityCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;displayParityCouponOffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;onPurchaseComplete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;displayPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;seriesPackageLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;showClaimCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;purchasing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;clearNotifications&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;usePackage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can tell that theres a lot going on under the hood. We passed a &lt;code&gt;sellable&lt;/code&gt;, &lt;code&gt;quantity&lt;/code&gt;, and &lt;code&gt;authToken&lt;/code&gt; to the hook. A sellable is something that has a &lt;code&gt;price&lt;/code&gt; and a url on the object to check that price along with a url to make the purchase.&lt;/p&gt;

&lt;p&gt;For the internals, heres the stack of hooks that I ended up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;useAvailableCoupons.tsx&lt;/li&gt;
&lt;li&gt;useCouponFromHeader.tsx&lt;/li&gt;
&lt;li&gt;useDefaultCoupon.tsx&lt;/li&gt;
&lt;li&gt;useFetchPackageForSellable.tsx&lt;/li&gt;
&lt;li&gt;usePackage.tsx&lt;/li&gt;
&lt;li&gt;usePackageCheckout.tsx&lt;/li&gt;
&lt;li&gt;usePackageReducer.tsx&lt;/li&gt;
&lt;li&gt;useParityCoupon.tsx&lt;/li&gt;
&lt;li&gt;useRequest.tsx&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;usePackage&lt;/code&gt; is the hook that orchestrated everything. The basic flow was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;receive a &lt;code&gt;sellable&lt;/code&gt; from props&lt;/li&gt;
&lt;li&gt;instantiate initial state&lt;/li&gt;
&lt;li&gt;fetch the current price of the sellable&lt;/li&gt;
&lt;li&gt;check for an applied coupon&lt;/li&gt;
&lt;li&gt;extract available coupons&lt;/li&gt;
&lt;li&gt;extract purchase power parity (PPP) coupon&lt;/li&gt;
&lt;li&gt;create a function to handle purchase complete&lt;/li&gt;
&lt;li&gt;create a function for when the coupon is applied&lt;/li&gt;
&lt;li&gt;return display price, functions, and other relevant data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The main areas being: load the most recent price, handle any available coupons, give the user of the hook information about everything that's happening.&lt;/p&gt;

&lt;p&gt;Most of these hooks are use effects waiting for changes of the particular state they manage. Lets take a look at the &lt;code&gt;useParityCoupon&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useParityCoupon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DispatchType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;availableCoupons&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;CouponType&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parityCoupon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;availableCoupons&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;coupon_region_restricted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parityCoupon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countryCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parityCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;coupon_region_restricted_to&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;countryName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parityCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;coupon_region_restricted_to_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayParityCouponOffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countryName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="nx"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;countryCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="nx"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parityCoupon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SET_PARITY_COUPON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;displayParityCouponOffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;parityCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;countryName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;availableCoupons&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may notice one of the bugs that was in our purchase flow. &lt;code&gt;availableCoupons&lt;/code&gt; is of type &lt;code&gt;CouponType[]&lt;/code&gt; which &lt;em&gt;is not a stable value&lt;/em&gt;. React will shallowly compare this reference. When this hook runs again, &lt;code&gt;availableCoupons&lt;/code&gt; will always be different. These hooks were scattered with issues like this. This is one of the foot guns that made debugging these hooks difficult.&lt;/p&gt;

&lt;p&gt;I ran into issues testing this code in EpicReact. One being, the parity coupon was not being set when it should have been. When the value you expect to be there isn't, you have to go inspect what could be affecting it. In this case, I had to inspect these hooks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;usePackage.tsx&lt;/li&gt;
&lt;li&gt;useFetchPackageForSellable.tsx&lt;/li&gt;
&lt;li&gt;useAvailableCoupons.tsx&lt;/li&gt;
&lt;li&gt;usePackageReducer.tsx&lt;/li&gt;
&lt;li&gt;useParityCoupon.tsx&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tracing the data through all of these hooks is a nightmare. First you check if &lt;code&gt;usePackage&lt;/code&gt; is calling &lt;code&gt;useParityCoupon&lt;/code&gt; correctly. Next, we have to check if the values from &lt;code&gt;useFetchPackageForSellable&lt;/code&gt; are setting state in &lt;code&gt;usePackageReducer&lt;/code&gt; correctly. Then I had to make sure that &lt;code&gt;useAvailableCoupons&lt;/code&gt; set the coupons correctly and finally that &lt;code&gt;useParityCoupon&lt;/code&gt; was sending the correct event when it was supposed to. This took a lot of &lt;code&gt;debugger&lt;/code&gt; and &lt;code&gt;console.log&lt;/code&gt; statements to just figure out what the flow of data was. &lt;/p&gt;

&lt;p&gt;On top of this, we have to make sure that when the user applies the PPP coupon, we refetch the price all over again.&lt;/p&gt;

&lt;p&gt;All of this had to be stored in my head before I could start making any changes. &lt;/p&gt;

&lt;h2&gt;
  
  
  XState Saves the Day
&lt;/h2&gt;

&lt;p&gt;One of the first things you will notice when using the XState version of this hook is how much simpler the api is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCommerceMachine&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;XState just needs the &lt;code&gt;sellable&lt;/code&gt; to kick off the price checking process.&lt;/p&gt;

&lt;p&gt;XState forces you to think about your discreet states. Theres a big difference between the &lt;code&gt;context&lt;/code&gt; you have around your state machine and the &lt;code&gt;states&lt;/code&gt; your machine can be in.&lt;/p&gt;

&lt;p&gt;Everything I described above can be boiled down into these states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fetchingPrice&lt;/li&gt;
&lt;li&gt;priceLoaded&lt;/li&gt;
&lt;li&gt;startingPurchase&lt;/li&gt;
&lt;li&gt;handlingPurchase&lt;/li&gt;
&lt;li&gt;success&lt;/li&gt;
&lt;li&gt;failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We use these states to build up the &lt;code&gt;context&lt;/code&gt; of our state machine. This is what we want to track in our state machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sellable&lt;/li&gt;
&lt;li&gt;purchaseHeaders&lt;/li&gt;
&lt;li&gt;error: null&lt;/li&gt;
&lt;li&gt;price: null&lt;/li&gt;
&lt;li&gt;appliedCoupon: null&lt;/li&gt;
&lt;li&gt;notification: null&lt;/li&gt;
&lt;li&gt;email: null&lt;/li&gt;
&lt;li&gt;stripeToken: null&lt;/li&gt;
&lt;li&gt;quantity: 1&lt;/li&gt;
&lt;li&gt;purchase: null&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see &lt;code&gt;sellable&lt;/code&gt; and &lt;code&gt;purchaseHeaders&lt;/code&gt; are all passed in from a closure above. Heres what the basic state machine with no transitions looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createCommerceMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;purchaseHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;createMachine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;commerceMachine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetchingPrice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;purchaseHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;stripeToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;purchase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;states&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;fetchingPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="na"&gt;checkingPriceData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="na"&gt;priceLoaded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="na"&gt;startingPurchase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="na"&gt;handlingPurchase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="na"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;guards&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may notice that this &lt;code&gt;createCommerceMachine&lt;/code&gt; function takes more arguments than our &lt;code&gt;useCommerceMachine&lt;/code&gt; hook and thats because we create an intermediate hook to load authentication and such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useCommerceMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useEggheadUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sellableSlug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;slug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commerceMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;purchaseHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;createCommerceMachine&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;purchaseHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;stripeToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRIPE_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sellableSlug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;useMachine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commerceMachine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are memoizing our call to &lt;code&gt;createCommerceMachine&lt;/code&gt; because we only want to create a new machine if the &lt;code&gt;sellableSlug&lt;/code&gt; or the &lt;code&gt;userID&lt;/code&gt; has changed.&lt;/p&gt;

&lt;p&gt;The first machine initializes in the &lt;code&gt;fetchingPrice&lt;/code&gt; state. This is a state that is invoking a promise (&lt;a href="https://xstate.js.org/docs/guides/communication.html#invoking-promises"&gt;xstate docs&lt;/a&gt;). A state can invoke a number of services but in our case we are using a promise. Heres the overview of the state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fetchingPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetchPrice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;// return a promise here},&lt;/span&gt;
        &lt;span class="na"&gt;onDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkingPriceData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="c1"&gt;// do something with the resulting data],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="c1"&gt;// do something if the promise throws an error]&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that &lt;code&gt;invoke&lt;/code&gt; takes a &lt;code&gt;src&lt;/code&gt; promise. XState will handle calling this function and handing the resulting data to &lt;code&gt;onDone&lt;/code&gt; or &lt;code&gt;onError&lt;/code&gt;. This is where we calculate the &lt;code&gt;context.price&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;onDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkingPriceData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;adjustPriceForUpgrade&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use XState's &lt;code&gt;assign&lt;/code&gt; function to take the data that the &lt;code&gt;event&lt;/code&gt; gave back and &lt;code&gt;assign&lt;/code&gt; it to the price. We have to handle a case where we adjust the price if the user is upgrading a purchase. I do this in separate action because I like to see all the different things that are happening when I read the machine. You could technically do this action in the &lt;code&gt;assign&lt;/code&gt; above, but then you aren't optimizing for deletion. &lt;/p&gt;

&lt;p&gt;The next state is &lt;code&gt;checkingPriceData&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;checkingPriceData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;always&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;cond&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;couponErrorIsPresent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;setErrorFromCoupon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;priceLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkForDefaultCoupon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a transient state. We use &lt;code&gt;always&lt;/code&gt; to make a transient transition (&lt;a href="https://xstate.js.org/docs/guides/transitions.html#transient-transitions"&gt;xstate docs&lt;/a&gt;). This is a spot in our state machine where if some condition is true with the current context, we want to conditionally send it to another state. The first condition to return true will be the transition that's executed. The default is to send to &lt;code&gt;priceLoaded&lt;/code&gt; state because there is no condition preventing this from happening.&lt;/p&gt;

&lt;p&gt;We defined our &lt;code&gt;couponErrorIsPresent&lt;/code&gt; guard in our &lt;code&gt;guards&lt;/code&gt; object below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;guards&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;couponErrorIsPresent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;coupon_error&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All it needs to do is return true or false. We check the specific data we need to see if a &lt;code&gt;coupon_error&lt;/code&gt; is present. If it is, we use &lt;code&gt;setErrorFromCoupon&lt;/code&gt; to set the &lt;code&gt;error&lt;/code&gt; context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;setErrorFromCoupon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows our UI to check the state of our machine and our context to determine if it needs to display an error.&lt;/p&gt;

&lt;p&gt;Moving along, we assume that our price check didnt return a coupon error, we move into the &lt;code&gt;priceLoaded&lt;/code&gt; state. This is the last state our machine will automatically transition to until it receives an event from the outside telling to to transition to another state. Heres everything the outside world can tell our state machine to do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;priceLoaded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;APPLY_COUPON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetchingPrice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;DISMISS_COUPON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetchingPrice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;SET_QUANTITY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetchingPrice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;START_PURCHASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;startingPurchase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;CLAIM_COUPON&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handlingPurchase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice that &lt;code&gt;APPLY_COUPON&lt;/code&gt;, &lt;code&gt;DISMISS_COUPON&lt;/code&gt;, and &lt;code&gt;SET_QUANTITY&lt;/code&gt; all just send the machine back to the &lt;code&gt;fetchingPrice&lt;/code&gt; state. This is one of the benefites of XState. We can reuse our logic on how we fetch the price but give it a slightly different &lt;code&gt;context&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Say our machine recieves the &lt;code&gt;APPLY_COUPON&lt;/code&gt; event. This event comes with &lt;code&gt;appliedCoupon&lt;/code&gt;. You can see that we are using &lt;code&gt;assign&lt;/code&gt; to add the &lt;code&gt;appliedCoupon&lt;/code&gt; from the event into our context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then our machine transitions back into the &lt;code&gt;fetchingPrice&lt;/code&gt; state. I mentioned earlier that &lt;code&gt;fetchingPrice&lt;/code&gt; &lt;code&gt;invokes&lt;/code&gt; a promise for us. Heres what the promise looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fetchingPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetchPrice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;priceCheckURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;egghead_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sellable_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sellable&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;priceCheckURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;pickBy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;sellables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="nx"&gt;sellable_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="nx"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appliedCoupon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;onDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that we are grabbing &lt;code&gt;quantity&lt;/code&gt;, &lt;code&gt;appliedCoupon&lt;/code&gt;, &lt;code&gt;sellable&lt;/code&gt;, and &lt;code&gt;upgradeFromSellable&lt;/code&gt; from our &lt;code&gt;context&lt;/code&gt; and passing some of those values to the body of our &lt;code&gt;axios.post&lt;/code&gt; call. This is how we can reuse our &lt;code&gt;fetchingPrice&lt;/code&gt; state, with different &lt;code&gt;context&lt;/code&gt; to fetch prices when no coupon is applied, when we've applied a coupon, or even when the quantity we are asking for has changed.&lt;/p&gt;

&lt;p&gt;When the user wants to start a purchase, we receive a &lt;code&gt;START_PURCHASE&lt;/code&gt; event. This event simply transitions us to the &lt;code&gt;startingPurchase&lt;/code&gt; state. We have this state so that we know when the user has clicked the "Purchase" button and a modal to accept their info is been created. &lt;/p&gt;

&lt;p&gt;While in the &lt;code&gt;startingPurchase&lt;/code&gt; state, we can do two things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;startingPurchase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;CANCEL_PURCHASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;priceLoaded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;HANDLE_PURCHASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handlingPurchase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;stripeToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stripeToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can either cancel the purchase and return to &lt;code&gt;priceLoaded&lt;/code&gt; or the user has entered their data and is attempting a purchase that we need to handle. In our case, we contact stripe for a token and then get the email that they entered. This is all we need to kick off our purchase process.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;handlingPurchase&lt;/code&gt; is a state that &lt;code&gt;invokes&lt;/code&gt; a promise to &lt;code&gt;POST&lt;/code&gt; data to our purchases api endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;handlePurchase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handlePurchase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;// return promise that makes the purchase},&lt;/span&gt;
    &lt;span class="na"&gt;onDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;purchase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sendToThanks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the same process we've described. We can either transition to &lt;code&gt;success&lt;/code&gt; or &lt;code&gt;failure&lt;/code&gt; based on the response of the purchase promise. If the purchase was successful, our specific UX is that we send the user to a &lt;code&gt;/thanks&lt;/code&gt; page.&lt;/p&gt;

&lt;p&gt;State machines are verbose. I haven't described every feature this machine does but in total, this file is 314 lines long. XState forces you to map our all our states and transitions explicitly. This affords you the ability to know exactly when something is happening. &lt;/p&gt;

&lt;p&gt;Earlier, when I had a problem with my custom &lt;code&gt;usePackage&lt;/code&gt; hook, I would have to follow all the hook calls to track the data and when things happened. In this case, say I am trying to apply a coupon but my machine loads the price, and the request comes back with a price I didn't expect. I can go to my machine and know exactly where coupons get applied to check if it's applied correctly and exactly where the coupon is being used in the request. There's no guessing involved. &lt;/p&gt;

&lt;p&gt;As feature requests come in, it's much easier to know exactly where they fit in. Say we want to add a feature so that the user can upgrade from one package to another. We need to send the package we are upgrading from to the server. We know we'll need to pass that package in from react:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCommerceMachine&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;upgradeFromSellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we know that we will need this object in our context inside of our machine so we can use it when we are fetching our price.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createCommerceMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;purchaseHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;upgradeFromSellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;createMachine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;commerceMachine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetchingPrice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;purchaseHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;upgradeFromSellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can use this &lt;code&gt;upgradeFromSellable&lt;/code&gt; object in our &lt;code&gt;fetchingPrice&lt;/code&gt; promise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fetchingPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetchPrice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;sellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;upgradeFromSellable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sellable&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;priceCheckURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;pickBy&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;sellables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;upgrade_from_sellable_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;upgradeFromSellable&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;upgrade_from_sellable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;upgradeFromSellable&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c1"&gt;// ...&lt;/span&gt;
              &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="c1"&gt;// ...&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;onDone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;// assign the returned price},&lt;/span&gt;
    &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="c1"&gt;// assign the error},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no guessing involved in where we need to put this object to affect our prices call. &lt;/p&gt;

&lt;p&gt;There is always state our UI's are dealing with, even if we are explicitly modeling it. State machines force you to model how you want your state to act and what can change the state in your machine. State machines expose the implicit state that you may or may not have known was there. &lt;/p&gt;

</description>
      <category>xstate</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Announcing the VS Code Snippets egghead Community Challenge</title>
      <dc:creator>Will Johnson</dc:creator>
      <pubDate>Tue, 22 Sep 2020 19:10:35 +0000</pubDate>
      <link>https://dev.to/egghead/announcing-the-vs-code-snippets-egghead-community-challenge-dh0</link>
      <guid>https://dev.to/egghead/announcing-the-vs-code-snippets-egghead-community-challenge-dh0</guid>
      <description>&lt;p&gt;We are excited to announce the first egghead Community Challenge!&lt;/p&gt;

&lt;p&gt;From today Sept 22st until Monday, October 5th, you will have a chance to win some badass prizes by creating VS Code snippets that are helpful for developers!&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Code Snippets?
&lt;/h2&gt;

&lt;p&gt;Code snippets are templates that make it easier to enter repeating code patterns, such as loops or conditional-statements.&lt;/p&gt;

&lt;p&gt;Learn more Code Snippets &lt;a href="https://code.visualstudio.com/docs/editor/userdefinedsnippets"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Participate?
&lt;/h2&gt;

&lt;p&gt;One of the primary principles of egghead is that we are a "community of practice". One of the tenets of a community of practice is sharing resources that make us better at our craft. &lt;/p&gt;

&lt;p&gt;Code snippets speed up workflow and productivity. Creating and sharing some useful snippets will help the community as a whole.&lt;/p&gt;

&lt;h2&gt;
  
  
  Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time-Saver:&lt;/strong&gt; Save developers time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Laugh out Loud:&lt;/strong&gt; This is something fun like emoji art&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pain Relief:&lt;/strong&gt; Take away any headache like REGEX&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prizes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3 Winners(1 per category)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 months subscription to egghead.io&lt;/li&gt;
&lt;li&gt;Full egghead Swag Pack from the egghead Swag Store(hat, beanies, stickers, t-shirt)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How To Submit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a GitHub repo name egghead-challenge-'yourreponame'&lt;/li&gt;
&lt;li&gt;Share the URL to the snippet in the Discord group&lt;/li&gt;
&lt;li&gt;Example here: &lt;a href="https://github.com/wjohnson85/egghead-challenge-will-snippet/blob/master/snippet-example/"&gt;https://github.com/wjohnson85/egghead-challenge-will-snippet/blob/master/snippet-example/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Support
&lt;/h2&gt;

&lt;p&gt;You will be invited to a group DM on discord where we will share resources to help get you to start creating and sharing snippets.&lt;/p&gt;

&lt;p&gt;You can also ask questions, get tips, and share ideas with other challenge members.&lt;/p&gt;

&lt;p&gt;I highly suggest sharing on social media your progress using #eggheadchallenge &lt;/p&gt;

&lt;h2&gt;
  
  
  Dates To Remember
&lt;/h2&gt;

&lt;p&gt;September 22: Community Challenge Begins!&lt;/p&gt;

&lt;p&gt;October 5th: Community Challenge Submissions Due at 11:59 PM PST.&lt;/p&gt;

&lt;p&gt;October 8th:: Community Challenge Winners Announced&lt;/p&gt;

&lt;p&gt;If you want to participate comment below and an egghead staff member will be in touch!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How To Get Started with AWS (Q&amp;A)</title>
      <dc:creator>Will Johnson</dc:creator>
      <pubDate>Wed, 24 Jun 2020 18:28:43 +0000</pubDate>
      <link>https://dev.to/egghead/how-to-get-started-with-aws-q-a-56k1</link>
      <guid>https://dev.to/egghead/how-to-get-started-with-aws-q-a-56k1</guid>
      <description>&lt;p&gt;To celebrate the release of Tomasz Łakomy's new AWS course &lt;a href="https://egghead.io/courses/build-an-app-with-the-aws-cloud-development-kit"&gt;Build an App with the AWS Cloud Development Kit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We hosted an AWS Q&amp;amp;A on Twitter. The egghead web developer community on Twitter was able to ask questions, and Tomasz provided some great answers.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1272937541835067399-53" src="https://platform.twitter.com/embed/Tweet.html?id=1272937541835067399"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1272937541835067399-53');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1272937541835067399&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  I kicked off the discussion by asking "Can you use other databases besides DynamoDB with AWS?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tomasz:&lt;/strong&gt; &lt;br&gt;
Absolutely! DynamoDB is an AWS-exclusive NoSQL database (or key-value store if you'd like to be really accurate) but for instance.&lt;/p&gt;

&lt;p&gt;You can use Amazon RDS (Relational Database Service) to use MySQL, PostgreSQL, MariaDB, Oracle (if you need to) and Amazon Aurora&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aws.amazon.com/rds/aurora/"&gt;Amazon Aurora&lt;/a&gt; is another type of database built for the cloud, unlike DynamoDB it's a MySQL and PostgreSQL-compatible relational database&lt;/p&gt;

&lt;h2&gt;
  
  
  Ceora asked for suggestions for getting practical/real-world experience in AWS?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/ceeoreo_"&gt;@ceeoreo_&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tomasz:&lt;/strong&gt; &lt;br&gt;
When it comes to getting actual experience: start experimenting&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use S3 to backup your photos&lt;/li&gt;
&lt;li&gt;Use Lambda to build an API for your website&lt;/li&gt;
&lt;li&gt;Use DynamoDB as a todo list&lt;/li&gt;
&lt;li&gt;Use SageMaker to appear incredibly smart because machine learning is magic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;He also added going through the &lt;a href="https://aws.amazon.com/certification/certification-prep/"&gt;AWS Certified Cloud Practitioner&lt;/a&gt; prep might be a good idea because the purpose of the exam is to get you familiar with AWS Cloud&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code Pixi and Vaibhav asked: "What are your recommendations for someone who's a total beginner with AWS?"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/TheCodePixi"&gt;@TheCodePixi&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/vaibhavThevedi"&gt;@Vaibhav&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tomasz:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, start with AWS Lambda - you don't have to become an expert, but you can start using it in order to (for instance) build your own serverless APIs&lt;/p&gt;

&lt;p&gt;It doesn't have to be a really deep dive, IMHO experimenting is an excellent way to learn (and you're not going to get charged anything on a new account thanks to the free tier - a million free requests per month). &lt;/p&gt;

&lt;p&gt;IMHO this egghead lesson &lt;a href="https://egghead.io/lessons/aws-wtf-is-aws-lambda"&gt;WTF is AWS Lambda&lt;/a&gt; by yours truly is a great place to start: &lt;/p&gt;

&lt;p&gt;Secondly, consider checking out some of the &lt;a href="https://aws.amazon.com/certification/certification-prep/"&gt;AWS Certified Cloud Practitioner courses&lt;/a&gt; - it's the easiest AWS certification exam and the goal of it is to show that you "have the knowledge and skills necessary to effectively demonstrate an overall understanding of the AWS Cloud"&lt;/p&gt;

&lt;p&gt;This course is also a great introduction into the world of serverless/AWS:&lt;br&gt;
&lt;a href="https://egghead.io/courses/develop-a-serverless-backend-using-node-js-on-aws-lambda"&gt;Develop A Serverless Backend Using Node JS on AWS Lambda&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Something more official:&lt;br&gt;
&lt;a href="https://aws.amazon.com/getting-started/fundamentals-overview/"&gt;AWS Fundamentals&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Aengus asked "What sort of use cases would best be served by using AWS vs something like Netlify or Heroku? When would it be overkill?"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/aengusmcmillin"&gt;@aengusmcmillin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tomasz:&lt;/strong&gt; &lt;br&gt;
"As with all great questions in programming, the answer is: "It depends"&lt;/p&gt;

&lt;p&gt;Netlify, Heroku, AWS Amplify solve similar problems when it comes to building web apps and they are amazing at that. My personal site uses Netlify, but all of our infrastructure at OLX Tech lives on AWS since we have a lot of traffic.&lt;/p&gt;

&lt;p&gt;Of course, to get started with AWS you don't have to be a huge company, thanks to their free tier (my last bill was $5 though). Even if you use Netlify, IMHO there's a great value of getting to know serverless technologies a bit better. &lt;/p&gt;

&lt;p&gt;I wanted to say that serverless is the future, but it's not entirely true serverless is here now and it'll stay for a while"&lt;/p&gt;

&lt;h2&gt;
  
  
  Kranthiasks "Is it possible to build complex applications completely using AWS lambda/serverless?" and "Can you refer to any resources related to architecting fully serverless applications?"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/kkranthi438"&gt;@kkranthi438&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tomasz:&lt;/strong&gt; &lt;br&gt;
Yup! It definitely requires a shift in perspective when it comes to thinking about your architecture (you don't have a server running your monolith 24/7). &lt;/p&gt;

&lt;p&gt;For example, at OLX Tech we're running serverless services which are built on top of multiple lambdas + DynamoDB&lt;/p&gt;

&lt;p&gt;For resources read and watch everything that &lt;a href="https://twitter.com/theburningmonk"&gt;@theburningmonk&lt;/a&gt; has even written/recorded. As well as this &lt;a href="https://egghead.io"&gt;eggheadio&lt;/a&gt; course: &lt;br&gt;
&lt;a href="https://egghead.io/courses/building-serverless-web-applications-with-react-aws-amplify"&gt;Building Serverless Web Applications With React AWS Amplify&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lauro asked: "Is it worth getting AWS certified?"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/laurosilvacom"&gt;@laurosilvacom&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tomasz:&lt;/strong&gt; &lt;br&gt;
IMHO it is - it's an excellent way of getting and solidifying your knowledge about AWS Cloud. Having a concrete list of services that you need to understand is better than "I'm going to learn all of it!". &lt;/p&gt;

&lt;p&gt;I wrote a bit more about my experience in this post: &lt;a href="https://tlakomy.com/passing-aws-solutions-architect-associate-exam"&gt;Passing AWS Solutions Architect Associate exam&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  John wanted to know "Did diving deep clear up any misunderstandings you had going in?"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/johnlindquist"&gt;@johnlindquist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tomasz:&lt;/strong&gt; &lt;br&gt;
Absolutely! For instance, I considered S3 to be an actual 'simple' service: "just buckets that store files, how hard can that be". While that's true on a surface level, S3 has much more to offer when it comes to features like versioning, encryption, different storage tiers&lt;/p&gt;

&lt;p&gt;Another thing, I had this idea that since DynamoDB is a NoSQL database it's really flexible and you don't need to think about your access patterns before you start using it. As Alex DeBrie (&lt;a href="https://twitter.com/alexbdebrie"&gt;@alexbdebrie&lt;/a&gt;) explains in his excellent "DynamoDB Book", this is not true&lt;/p&gt;

&lt;h2&gt;
  
  
  Tyler asks, "What are some features you wish AWS would add or possibly features that are coming out that you are excited about?"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://twitter.com/iamtylerwclark"&gt;@iamtylerwclark&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tomasz:&lt;/strong&gt; &lt;br&gt;
I'd love to see a CloudWatch console visual overhaul, currently, it's not exactly intuitive and it could definitely use some love. Apart from that (it's a common complaint, I'm not exactly unique in that regard) AWS docs are famously... not approachable for beginners (Which is actually part of the motivation behind my course. I went through all of that so you don't have to!). &lt;/p&gt;

&lt;p&gt;I'd also love to see more educational content from AWS featured in the Console - for instance when you go to DynamoDB console for the first time.&lt;/p&gt;

&lt;p&gt;And yes, there are various conference talks, webinars, etc but what I feel folks are lacking are guidance when it comes to "I have all those features, which ones are going to solve my problems"&lt;/p&gt;

&lt;p&gt;One more thing: Dear AWS, please make the SAVE button in AWS Lambda Console bigger, make it blink, jump, or whatever when I have unsaved changes in the editor. Don't make me debug code that is not even saved :(&lt;/p&gt;

&lt;p&gt;These were some really great questions and Tomasz provided some great insight on how to get started learning AWS!&lt;/p&gt;

&lt;p&gt;His course &lt;a href="https://egghead.io/courses/build-an-app-with-the-aws-cloud-development-kit"&gt;Build an App with the AWS Cloud Development Kit&lt;/a&gt; is available now!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Looking for code streamers? Here's a list for you!</title>
      <dc:creator>Will Johnson</dc:creator>
      <pubDate>Fri, 17 Apr 2020 22:35:04 +0000</pubDate>
      <link>https://dev.to/egghead/looking-for-code-streamers-here-s-a-list-for-you-4jdi</link>
      <guid>https://dev.to/egghead/looking-for-code-streamers-here-s-a-list-for-you-4jdi</guid>
      <description>&lt;p&gt;Code streaming has become very popular.&lt;/p&gt;

&lt;p&gt;More and more people are streaming themselves, solving problems, building things, and interacting with the developer community. They are starting channels on Mixer, Twitch, and YouTube.&lt;/p&gt;

&lt;p&gt;We wanted to create a resource of code streamers that can be found all in one place!&lt;/p&gt;

&lt;p&gt;We asked the &lt;a href="[https://community.egghead.io/t/code-streamers-tell-us-where-to-check-out-your-stream/2144](https://community.egghead.io/t/code-streamers-tell-us-where-to-check-out-your-stream/2144)"&gt;egghead community&lt;/a&gt; to share their streams.&lt;/p&gt;

&lt;p&gt;Do you stream? Leave a link to your stream in the comments!&lt;/p&gt;

&lt;p&gt;Here are some streamers from the egghead community!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F1250239000423608321%2FpVrygbIs_400x400.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F1250239000423608321%2FpVrygbIs_400x400.jpg" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/pachicodes" rel="noopener noreferrer"&gt;Pachi&lt;/a&gt; is self-taught developer who loves CSS(who doesn't, though?). She streams herself creating small pages and sometimes playing League of Legends, being a new and showing her drive it's an excellent opportunity to watch someone on their way to big things!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/pachicodes" rel="noopener noreferrer"&gt;https://www.twitch.tv/pachicodes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftylerclark.dev%2Fstatic%2F5feb9c82f5ca3b5cad64df57682248a3%2F20455%2Fprof.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Ftylerclark.dev%2Fstatic%2F5feb9c82f5ca3b5cad64df57682248a3%2F20455%2Fprof.webp" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/iamtylerwclark" rel="noopener noreferrer"&gt;Tyler Clark&lt;/a&gt; is DevRel Engineer at Auth0. He pairs up with other developers and open source maintainers to learn new tools and technologies in real-world apps. His topics are very diverse in subjects, from tools like React-Query to mastering codesandbox or illustrating development concepts. There's something for every developer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/livewithtyler" rel="noopener noreferrer"&gt;https://www.twitch.tv/livewithtyler&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F835711461515231232%2F52DhKCvR_400x400.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F835711461515231232%2F52DhKCvR_400x400.jpg" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/ETBoggs" rel="noopener noreferrer"&gt;Eric Boggs&lt;/a&gt; teaches free intro to Web Dev lessons. All you need a VSCode and a good attitude. No experience required. This would be an excellent way to introduce someone you know to web dev. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.quarantinecode.com/" rel="noopener noreferrer"&gt;https://www.quarantinecode.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F715433404100059137%2FAYjfw8tH_400x400.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F715433404100059137%2FAYjfw8tH_400x400.jpg" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/jlengstorf" rel="noopener noreferrer"&gt;Jason Lengstorf&lt;/a&gt; hosts the Learn With Jason show where his guest either teach him something new or he teaches them something new in 90 mins. Jason has a great community around him, and his chat is super enthusiastic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitch.tv/jlengstorf" rel="noopener noreferrer"&gt;https://twitch.tv/jlengstorf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars0.githubusercontent.com%2Fu%2F17270662%3Fs%3D460%26u%3D8d1a4d67576db0a3baa21fa5b2ecab811476da61%26v%3D4" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars0.githubusercontent.com%2Fu%2F17270662%3Fs%3D460%26u%3D8d1a4d67576db0a3baa21fa5b2ecab811476da61%26v%3D4" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/GirlsCodeMK" rel="noopener noreferrer"&gt;Eva&lt;/a&gt; is a co-founder of includesJS focused on improving diversity in tech. Eva host online conversations on Twitch with developers, organizers, conference speakers, and more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/edieblu" rel="noopener noreferrer"&gt;https://www.twitch.tv/edieblu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd2eip9sf3oo6c2.cloudfront.net%2Finstructors%2Favatars%2F000%2F000%2F275%2Fmedium%2Fheadshot_post_gym_dec_2018_500.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd2eip9sf3oo6c2.cloudfront.net%2Finstructors%2Favatars%2F000%2F000%2F275%2Fmedium%2Fheadshot_post_gym_dec_2018_500.png" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/chrisbiscardi" rel="noopener noreferrer"&gt;Chris Biscardi&lt;/a&gt; streams the cutting edge. He is very interested in new technologies and seeing how they crack. On his stream, you can learn about more than what's hot, you learn about what's going to be hot so you can be ahead of the curve.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/chrisbiscardi" rel="noopener noreferrer"&gt;https://www.twitch.tv/chrisbiscardi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F1248315803822219264%2F9Kz1jZMn_400x400.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F1248315803822219264%2F9Kz1jZMn_400x400.jpg" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/Rahatcodes" rel="noopener noreferrer"&gt;Rahat Chowdhury&lt;/a&gt; is a full stack developer and podcast host he streams himself building actual projects with in-demand technologies like React, Gatsby, and GraphQL. In one series, he showed behind the scenes of building his podcast site!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/rahatcodes" rel="noopener noreferrer"&gt;https://www.twitch.tv/rahatcodes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F1225193428159467521%2FixruMJfw_400x400.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F1225193428159467521%2FixruMJfw_400x400.jpg" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/kurtkemple" rel="noopener noreferrer"&gt;Kurt Kemple&lt;/a&gt; streams a variety of coding, design, video/photo editing, fitness, and a bit of gaming. So if you're not in the mood for just development, you can join his community for other topics as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/theworstdev" rel="noopener noreferrer"&gt;https://www.twitch.tv/theworstdev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.refactr.tech%2Fstatic%2Fa074c5e4ae73016d8eae38a17810cac6%2F49b36%2Fsamjulien600.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.refactr.tech%2Fstatic%2Fa074c5e4ae73016d8eae38a17810cac6%2F49b36%2Fsamjulien600.jpg" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/samjulien" rel="noopener noreferrer"&gt;Sam Julien&lt;/a&gt; is a DevReal at Auth0 he streams coding (mostly Angular &amp;amp; React), talk-building, and tech career information. He decided to rebuild his blog in Gatsby and let the community join him in the process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/samjulien" rel="noopener noreferrer"&gt;https://www.twitch.tv/samjulien&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F803994875335163904%2FqmmR8j-F_400x400.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F803994875335163904%2FqmmR8j-F_400x400.jpg" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/khaled_garbaya" rel="noopener noreferrer"&gt;Khaled Garbaya&lt;/a&gt; if you're interested in learning JAMStack, this would be the stream to follow.  Khaled builds real projects live from start to finish using the JAMStack, and you don't miss a thing! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/kgarbaya" rel="noopener noreferrer"&gt;https://www.twitch.tv/kgarbaya&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F1244297362203344898%2F5WpDRe1n_400x400.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F1244297362203344898%2F5WpDRe1n_400x400.png" alt="Alt text of image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/ryanwarnercodes" rel="noopener noreferrer"&gt;Ryan Warner&lt;/a&gt; for developers looking to improve their design skill's Ryans stream in an excellent place to start. Ryan starts working in Figma and brings designs to life with code. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.twitch.tv/RyanWarnerCodes" rel="noopener noreferrer"&gt;https://www.twitch.tv/RyanWarnerCodes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give these streamers a follow and get involved in their chat!&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
