<?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: Summerbud</title>
    <description>The latest articles on DEV Community by Summerbud (@summerbud).</description>
    <link>https://dev.to/summerbud</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F917046%2F599135a1-7e14-41a2-a4ae-ace771a619f0.jpeg</url>
      <title>DEV Community: Summerbud</title>
      <link>https://dev.to/summerbud</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/summerbud"/>
    <language>en</language>
    <item>
      <title>My problem about telling others to blindly read source code</title>
      <dc:creator>Summerbud</dc:creator>
      <pubDate>Fri, 22 Sep 2023 22:00:00 +0000</pubDate>
      <link>https://dev.to/summerbud/my-problem-about-telling-others-to-blindly-read-source-code-n0h</link>
      <guid>https://dev.to/summerbud/my-problem-about-telling-others-to-blindly-read-source-code-n0h</guid>
      <description>&lt;p&gt;“You should read the famous repo’s source code. Just read them, you will grow” I keep hitting this suggestion lately and it’s bothering me.&lt;/p&gt;

&lt;p&gt;My problem with this suggestion is without any goal in mind, blindly reading source code won’t help and will even cause harm. You need to have a goal in mind to make the magic work. Like “Hey, I want to learn about how to get the header using remark”. For this question, I found out Astro.js has an &lt;code&gt;Astro.glob&lt;/code&gt; function, the returns have a getHeadings function. How about I dig inside this function to see how it works?&lt;/p&gt;

&lt;p&gt;Another example. Recently, I needed to implement an Auth-Guard middleware, I immediately remembered that Auth0 provided a similar function. So I go to their repo to see how it can help me to implement my middleware and it is indeed very helpful.&lt;/p&gt;

&lt;p&gt;To me, the better version of this suggestion would be “When you encounter services that have good quality and open-source, list it down but read their source code when you need them”.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Playwright tips that will make your life easier - including how to select, act and combat flaky test</title>
      <dc:creator>Summerbud</dc:creator>
      <pubDate>Tue, 11 Oct 2022 01:37:30 +0000</pubDate>
      <link>https://dev.to/summerbud/playwright-tips-that-will-make-your-life-easier-including-how-to-select-act-and-combat-flaky-test-4mpo</link>
      <guid>https://dev.to/summerbud/playwright-tips-that-will-make-your-life-easier-including-how-to-select-act-and-combat-flaky-test-4mpo</guid>
      <description>&lt;p&gt;Recently I began to help our company implement integration tests and encounter dozens of issues that bother me a lot. Including how to correctly assert elements. How to efficiently wait for an element and most important of all, how to combat the flaky tests.&lt;/p&gt;

&lt;p&gt;I found out there has not many resources to discuss this kind of topic and I dip very deep into Playwright issues, discussion, and documentation to find these tips. I think they are all very useful and they makes my life easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  General tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use locator!
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The locator will activate the auto-wait feature and the retry ability.&lt;/li&gt;
&lt;li&gt;The locator will stay up-to-date even if the DOM has changed. The example below shows that the locator of submit button will track the most up-to-date element. This makes the test smoother and reduces the possibility of a flaky test.
&lt;/li&gt;
&lt;/ul&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;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text=Submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hover&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// This uses ElementHandler&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;text=Submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  

&lt;span class="c1"&gt;// handle will always point to the non-hoverable/stale element &lt;/span&gt;
&lt;span class="c1"&gt;// which may cause issues&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hover&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Playwright auto-wait feature
&lt;/h3&gt;

&lt;p&gt;In short, the auto-wait feature means "Hey, when you are going to perform some behavior like CLICK, FILL or HOVER... etc, we will check whether the element is in the state that can carry on these tasks or not, if the conditions met we will perform the behavior, otherwise we will wait until the timeout exceed." &lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Before I notice this feature I write some tests 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="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is BAD&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="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;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// I will check whether button is enabled even it is always &lt;/span&gt;
    &lt;span class="c1"&gt;// enbled&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEnabled&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&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 code is bad not only you are wasting your testing time on a non-necessary assertion but also cause some unreliable consequence in some other scenario like fill in the field. You should rely on Playwright's auto-wait nature and let them do the heavy lifting work.&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is GOOD&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="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;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Next&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Click the button and let playwright do the actionability &lt;/span&gt;
    &lt;span class="c1"&gt;// checking&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Don't test for playwright
&lt;/h3&gt;

&lt;p&gt;Sometimes it feels tempting to make sure every step fulfills your needs like the 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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is not that good&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="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;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input#email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks normal and even responsible at the first glance. But you are testing for Playwright. Due to the nature of auto-waiting Playwright will check whether the field can input or not. If it is not it will be timeout at the filling step, so I think generally speaking this assertion is not necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Always use Promise.all to avoid race condition
&lt;/h3&gt;

&lt;p&gt;Because we are all in an async function when we utilize Playwright, there will have race conditions if we don't control the flow. &lt;sup id="fnref2"&gt;2&lt;/sup&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this will have race condition&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="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;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&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 above snippet showcases a scenario if somehow the URL response is before we click the next button (Playwright is a very fast machine), the &lt;code&gt;await page.waitForResponse("url")&lt;/code&gt; will be stall. In order to eliminate this kind of race condition, we should utilize &lt;code&gt;Promise.all&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this will have race condition&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="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;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="c1"&gt;// We start waiting for response&lt;/span&gt;
        &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="c1"&gt;// We click the button&lt;/span&gt;
        &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&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;In this way, we can safely make sure we wait for the click and the response is finished. None of them will be left behind.&lt;/p&gt;

&lt;p&gt;But you should not put multiple actions in one Promise.all(), It will act like multiple people controlling the keyboard at the same time which causes the un-predictable result. &lt;sup id="fnref3"&gt;3&lt;/sup&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is bad&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="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;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input#email&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;nameField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input#name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;nameField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hi&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;h3&gt;
  
  
  Use locator.waitFor if you have the visual indicator
&lt;/h3&gt;

&lt;p&gt;There are dozens of waitFor in the Playwright &lt;sup id="fnref4"&gt;4&lt;/sup&gt; and they are very useful in different contexts. In my journey of implementing the integration tests, I found out that &lt;code&gt;locator.waitFor&lt;/code&gt; is very reliable if you have a visual indicator. I will take a submit form for example. The form is very easy, fill in the email, click submit button, send the request to the server, respond with userID, and display a welcome message. The test may look like the 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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this have possibility to be flaky&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="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;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input#email&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;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user-end-point&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&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;Let's say we have a user endpoint on &lt;code&gt;http://localhost:3000/user&lt;/code&gt;. When creating the user we will post request to this endpoint. When getting the user information we will get request to this endpoint too. If we still need to constantly get the user's information on this form or if there has global state management which will constantly utilize this endpoint, Playwright may feel confused.&lt;/p&gt;

&lt;p&gt;Take the test below as an example. It will be flaky if in the middle of the &lt;code&gt;Promise.all&lt;/code&gt; Playwright did get the response but it's not due to the submission.&lt;/p&gt;

&lt;p&gt;The safest way of doing so is by relying on a visual indicator and use &lt;code&gt;locator.waitFor&lt;/code&gt;&lt;sup id="fnref5"&gt;5&lt;/sup&gt; to wait for the indicator to show up on the screen.&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is stable&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="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;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input#email&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;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&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;succeedMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h3&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Succeed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nx"&gt;succeedMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&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;h2&gt;
  
  
  How to combat flaky test
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Examine the root cause
&lt;/h3&gt;

&lt;p&gt;The root cause of the flaky test is diverse. It's hard to list them all in this article. But I can share some tips about how to find them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Use playwright debug mode
&lt;/h4&gt;

&lt;p&gt;There are two different kinds of debug modes in Playwright.&lt;sup id="fnref6"&gt;6&lt;/sup&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;code&gt;npx playwright test --debug&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This is the killer feature of Playwright, it will display a debug inspector to let you observe what the browser actually did in every step. Use this mode to check whether your locator is correct!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PWDEBUG=1 npx playwright test&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;In short, What PWDEBUG mode does is "Hey I will run the browser in headed mode and disable the timeout, so the browser won't close and your test flow won't be interrupted." This will come in handy if you just want to make sure every step of your test is correct. &lt;sup id="fnref7"&gt;7&lt;/sup&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick one that suits your needs, but remember in &lt;code&gt;--debug&lt;/code&gt; mode your click action is much slower than the real testing actions some tests may pass in &lt;code&gt;--debug&lt;/code&gt; mode but fail when you run the test without it. &lt;/p&gt;

&lt;h4&gt;
  
  
  Utilize --repeat-each to observe flaky test
&lt;/h4&gt;

&lt;p&gt;The way to observe a flaky test is by repeatedly operating a test. You can achieve that with &lt;code&gt;npx playwright test --repeat-each=&amp;lt;time_you_want_to_run&amp;gt;&lt;/code&gt;. When you find a set of tests is flaky, I recommend using this flag and focusing on one test at a time. &lt;sup id="fnref8"&gt;8&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;You could point to the specific test by using a colon and a line number. Take the snippet below as an example you could specifically test with it&lt;code&gt;npx playwright test form.spec.ts:13&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is stable&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// This is at line 13 of form.spec.ts&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input#email&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;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&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;succeedMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h3&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Succeed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nx"&gt;succeedMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&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;h4&gt;
  
  
  Use page.on(”console”) to load useful information
&lt;/h4&gt;

&lt;p&gt;You may have many &lt;code&gt;console.log&lt;/code&gt; around your code to make you know better what happened in the frontend. You can use this line of code to log them in the terminal.&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;console&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="nx"&gt;m&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BROWSER: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Make sure every steps are correctly waited.
&lt;/h4&gt;

&lt;p&gt;I found out this is usually the number one factor that causes a flaky test. Just like I mentioned above, utilize &lt;code&gt;Promise.all()&lt;/code&gt; to correctly wait for the test.&lt;/p&gt;

&lt;h4&gt;
  
  
  Utilize your trace
&lt;/h4&gt;

&lt;p&gt;Playwright provides &lt;code&gt;--trace&lt;/code&gt; flag to let you know what happened during the test. It will record every &lt;code&gt;console.log&lt;/code&gt;, network activity, and the action playwright conducted. The recommended way of doing the trace is only to record it when the first test is failing. Generally speaking, this is enough, but when you are facing a flaky test you will record the passed test (by definition flaky test means the first test failed but the second test passed) which is not helpful. &lt;sup id="fnref9"&gt;9&lt;/sup&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="c1"&gt;// Retry when the test failed&lt;/span&gt;
&lt;span class="k"&gt;import&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;PlaywrightTestConfig&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;@playwright/test&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;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PlaywrightTestConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;retries&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;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on-first-retry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;You could set the trace to on and keep the retry to one. In this way, you could observe the flakiness of the test and the trace of the test at the same time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Retry every test&lt;/span&gt;
&lt;span class="k"&gt;import&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;PlaywrightTestConfig&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;@playwright/test&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;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PlaywrightTestConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;retries&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;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test is natively fast, and it is really fast
&lt;/h3&gt;

&lt;p&gt;Disable some crucial action if your request hasn't finished yet. Let's say you have a form that needs to fetch a set of information to correctly construct a dropdown. In a normal situation, that request only takes a blink of time and the user won't even notice the dropdown's data is not complete.&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this is stable&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// This is at line 13 of form.spec.ts&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input#email&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;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Playwright may timeout in this action due to the request is not finished yet.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dropdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select#dropdown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dropdown&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;option-fetched-from-backend&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;succeedMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;h3&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Succeed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;


    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nx"&gt;succeedMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&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;In this scenario, your test may be flaky because the Playwright is too fast (You will certainly pass this test in debug mode) It only takes 100ms to finish the actions before selecting the dropdown and it begins to interact with the dropdown. If the request somehow is slower this time this test will fail because there doesn't have any data exist.&lt;/p&gt;

&lt;p&gt;The solution is to disable your dropdown when the data is not ready. With the auto-waiting nature of Playwright, it will wait a set amount of time, you can even expand the timeout specific to this action. &lt;sup id="fnref10"&gt;10&lt;/sup&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand your backend
&lt;/h3&gt;

&lt;p&gt;If your integration test involves interaction with the backend, some of the flakiness may come from there.&lt;/p&gt;

&lt;h4&gt;
  
  
  Check whether your backend can take multiple request at a time
&lt;/h4&gt;

&lt;p&gt;If your backend can only handle a single request at a time you should disable the parallel nature of Playwright. In your configuration file, you could achieve this through this configuration. &lt;sup id="fnref11"&gt;11&lt;/sup&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* Opt out of parallel tests */&lt;/span&gt;
    &lt;span class="na"&gt;workers&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Check if your operation need to be sequence
&lt;/h4&gt;

&lt;p&gt;If you want your test run in sequences you could leverage the native support of serial tests.&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;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This will be a test run in sequence&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test 1&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
    &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test 2&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="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;Or you could separate the test into different test files as a "Test List File", disable the parallel operation of Playwright like the above, and import these files in order. &lt;sup id="fnref12"&gt;12&lt;/sup&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Check if your backend have special issue
&lt;/h4&gt;

&lt;p&gt;In our backend, when we create a model we need to fetch the target model from GitHub or HuggingFace which will take an extra amount of time. Not only it take longer to finish, but it will also be affected by the internet condition at that moment. These all add up quickly and make the test exceed its timeout limit.&lt;/p&gt;

&lt;p&gt;You could have several ways to solve these problems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make some operation timeout much longer. Almost all playwright locators, waitfor, and expect have fine-grained timeout option. You can leverage this option to extend the timeout.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;this have possibility to be flaky&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="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;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input#email&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;submit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&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;hasText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user-end-point&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&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;ul&gt;
&lt;li&gt;Mock the data in the backend and don't test for the 3rd party interaction. I think this one is better compared to the first one. You could set up an env variable flag that indicates the whole backend session is for integration test and mock the model that takes extra time to create due to they are connecting with 3rd party source. In this way, your test will be quicker and more reliable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sum up
&lt;/h2&gt;

&lt;p&gt;I think there are three rings that rule them all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Promise.all&lt;/code&gt; to avoid race conditions.&lt;/li&gt;
&lt;li&gt;Disable your field when you haven't finished fetching data.&lt;/li&gt;
&lt;li&gt;Use the locator!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Alright, that is enough for today. Playwright is a very good tool. Hope you find the power of it.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/actionability"&gt;Playwright - Auto-waiting&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://github.com/microsoft/playwright/issues/5470"&gt;doc: Why use Promise.all when await works? #5470&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://github.com/microsoft/playwright/issues/12776"&gt;[BUG] Multiple type commands in one Promise.all do not resolve correctly #12776&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/api/class-page#page-wait-for-event"&gt;Playwright - Page&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/api/class-locator#locator-wait-for"&gt;Playwrihgt - locator.waitFor&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/debug"&gt;Playwright - Debug&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/debug#pwdebug"&gt;Playwright - Debug#PWDEBUG&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/test-cli#reference"&gt;Playwright - CLI#Reference&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn9"&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/trace-viewer-intro"&gt;Playwright - Trace Viewer&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn10"&gt;
&lt;p&gt;&lt;a href="https://rwoll.dev/posts/understanding-flaky-tests-and-avoiding-timeouts-with-playwright"&gt;End-to-End Testing: Fixing a Flaky Test and Avoiding Sleeps with Playwright&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn11"&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/test-parallel"&gt;Parallelism and sharding&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn12"&gt;
&lt;p&gt;&lt;a href="https://playwright.dev/docs/next/test-parallel#control-test-order"&gt;Playwright - Parallelism and sharding#Contro test order&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>test</category>
      <category>playwright</category>
      <category>javascript</category>
      <category>devops</category>
    </item>
    <item>
      <title>The spiral learning method, the beauty and the ugly</title>
      <dc:creator>Summerbud</dc:creator>
      <pubDate>Fri, 30 Sep 2022 01:48:07 +0000</pubDate>
      <link>https://dev.to/summerbud/the-spiral-learning-method-the-beauty-and-the-ugly-1ab1</link>
      <guid>https://dev.to/summerbud/the-spiral-learning-method-the-beauty-and-the-ugly-1ab1</guid>
      <description>&lt;p&gt;There are hundreds of learning methods that each have their strengths and weaknesses. but none of them suit me well enough like this one. I have adapted and developed it through my career as a programmer. I call it the spiral learning method(abbr. Spiral), borrowing from one of the casual chats with my friend. He used this metamorph to describe the way I learn and claim that it is very suitable to build new things.&lt;/p&gt;

&lt;p&gt;In short, what the Spiral does is learn when you NEED. Progress accordingly like climbing a spiral stair. Look up your GOAL which needs to be an achievable object. &lt;/p&gt;

&lt;p&gt;The origin of this method partially comes from my rebellion toward the learning method which our education adapts, I call it the linear learning method(abbr. Linear). Before we go into the details about the spiral let's talk about the linear way of learning things. I think the linear way of learning things does more harm than good to us. &lt;/p&gt;

&lt;h2&gt;
  
  
  The critic of linear learning
&lt;/h2&gt;

&lt;p&gt;The Linear is usually how the school teaches us to learn. It has pre-built guidelines for everyone. It becomes quite obvious when you take a look at how we structure our school grades with different progression. With this kind of structure ahead they claim that people can't reliably jump across different subjects and gain insight better than the child who learns things step by step. &lt;/p&gt;

&lt;p&gt;I think this is a big blind point in our education. It's true at some point of learning and partially true if you want to enter the route of the academy. But when it comes to how to apply knowledge and make something useful I feel the Linear will fall behind the Spiral in three ways.&lt;/p&gt;

&lt;h3&gt;
  
  
  Emphasizes knowledge has a pre-set structure
&lt;/h3&gt;

&lt;p&gt;The Linear emphasizes that the knowledge has a pre-set structure and the best way to acquire it is to grab it as a whole. But in reality, to accomplish or build something useful is not about knowing everything but knowing what you need and where to learn them. &lt;/p&gt;

&lt;p&gt;The Linear will cause people to think outside of school that there should be a similar structure to guide them. Something like Developer Roadmap&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. But usually, there is no such thing that exists. The knowledge is clustered. We need to cultivate the power to recognize what to learn and where to find them. Sadly our education fell short at this part.&lt;/p&gt;

&lt;h3&gt;
  
  
  The one fit for all end goal - learn knowledge as a whole
&lt;/h3&gt;

&lt;p&gt;The Linear has a pre-set end goal for everyone, that is learning knowledge as a whole. It's like when reading a novel, you have to finish it chapter by chapter. I think this end goal is not suitable for the rapidly changing environment, especially in programming. Besides that, not everyone will be motivated by the idea of learning knowledge as a whole. People should find a goal that supports their enthusiasm well.&lt;/p&gt;

&lt;h3&gt;
  
  
  The illusion that knowledge is fixed and doesn't change
&lt;/h3&gt;

&lt;p&gt;The Linear builds up an illusion that knowledge is fixed and it doesn't change at all. But in reality, every piece of knowledge is constantly changing and changing fast.&lt;/p&gt;

&lt;p&gt;The Linear makes people lose their curiosity and enthusiasm toward new knowledge. It nurtures generations of people who are not going to learn new things once they enter their career.&lt;/p&gt;

&lt;p&gt;In order to tackle this issue it may need a full scale of renewing our education and the learning journey after leaving the school. I think the Spiral is a good start. It is very good at inspiring and enlightening enthusiasm. &lt;/p&gt;

&lt;p&gt;In order to let you understand the difference between the Linear and the Spiral. I will showcase how they work in practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work in practice
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The linear learning method
&lt;/h3&gt;

&lt;p&gt;According to the Developer Roadmap, a beginner needs to learn "What is the Internet" first, then he can learn the three big giants in modern web development that is HTML, CSS, and Javascript. These are all big topics and may take months to knock on the door and years to master. &lt;/p&gt;

&lt;p&gt;After digging a little bit, the beginner can pick up the knowledge of version control and CSS processor then he can begin to learn the framework. The Web development framework usually describes lots of philosophy and thinking along with the documentation especially the React. Newcomers can read through these materials first then dive into the general components of these frameworks.&lt;/p&gt;

&lt;p&gt;Once the beginner learns some basic operations of the framework then he can try to think about what he wants to build.&lt;/p&gt;

&lt;h3&gt;
  
  
  The spiral learning method
&lt;/h3&gt;

&lt;p&gt;First, set up your goal. What drives you to web development? Drop by a fascinating website or feel passionate about gaming and would like to build a mini-game? Choose the goal that can enlighten your enthusiasm the most. In this section I will take building a personal blog for example.&lt;/p&gt;

&lt;p&gt;There are a bunch of tutorials about building your blog on Youtube. Pick one that suits your needs. Hopefully, they cover how to set up your development environment too. Follow the tutorial and finish it. In this way you could have a further understanding of what web dev looks like.&lt;/p&gt;

&lt;p&gt;Last, demo what you accomplish with your friend and family, ask for their feedback and move on. At this point, you may have a general understanding of HTML, CSS, and some frameworks. You will find out most of your knowledge is wrong but it's totally fine! Just set up a new goal and keep learning.&lt;/p&gt;

&lt;p&gt;At this point, I guess you could observe the difference between these two learning routes. The Linear has a pre-set learning route and encourages beginners to learn stuff step by step. The Spiral method challenges the linear way of learning and emphasizes the philosophy "Only learn things that can push the progression of your goal".&lt;/p&gt;

&lt;h2&gt;
  
  
  The methodology
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;In short, what the Spiral does is learn when you NEED. Progress accordingly like climbing a spiral stair. Look up your GOAL which needs to be an achievable object. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There have three sub-methodology of the Spiral:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to set up a clear and objective goal for your learning. Try to stay at the very edge of your ability and don't exceed too far. Judge your progression and the Effectiveness thought whether you achieve your goal.&lt;/li&gt;
&lt;li&gt;Progressively learn stuff that can help you achieve your goal and cultivate the ability to find where to learn them.&lt;/li&gt;
&lt;li&gt;When facing a set of knowledge, you don't need to learn them all. Just learn the stuff that pushes the progression of your goal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive into the details!&lt;/p&gt;

&lt;h3&gt;
  
  
  How to pick up your goal?
&lt;/h3&gt;

&lt;p&gt;This question is not about "How to find your goal", that is a very different question compared to this one. I am talking about a situation in which you may have multiple goals or ideas hanging around your mind and it's hard to find which one can bring the most benefit to you. If this is the case you could follow this step to find it out. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Please arrange these ideas into three categories with the difficulty to achieve them. And try to begin with the middle difficulty group.&lt;/li&gt;
&lt;li&gt;In the middle difficulty group, pick a random idea and examine it yourself with the following question: Can you imagine a blueprint that can help you achieve this goal? If you can fully imagine a blueprint that means you are familiar with this topic. In this case, we should leave this idea behind because you need to stand at the edge of your ability to maximize the efficiency of learning. You should try to find a goal so that it's quite easy to imagine its route at the start but remain a mystery afterward.&lt;/li&gt;
&lt;li&gt;Next! you may have a handful of goals and it's hard to choose which one again. I would suggest you choose a goal that you are most passionate about. In the end, enthusiasm is the most durable tactic to keep you learning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you pick up your goal we can start the journey.&lt;/p&gt;

&lt;h3&gt;
  
  
  Glasp full picture first, then deep dive
&lt;/h3&gt;

&lt;p&gt;To learn efficiently, you need to have some rough feeling about the direction. In other words, you need to have a map on hand. It's like a Minecraft map with minimum exploration which indicates where you are. Remember the way we learn how to build a blog? We go to Youtube and find some useful tutorials. At this point, you need to find something like it that explains the big picture of your ongoing journey. Some sort of full tutorial will greatly help you. (If your goal is too big you need to cut it into small pieces.) &lt;/p&gt;

&lt;p&gt;But sometimes they don't have this kind of tutorials. In this case you may need to find them in some articles, someone else's GitHub repository or from a book. Anything that can help you grasp the full picture will drastically push your progression.&lt;/p&gt;

&lt;p&gt;After you grasp the full picture. You could begin to dive deeper. Build much more complicated stuff and learn more.&lt;/p&gt;

&lt;h2&gt;
  
  
  The caveat of the spiral learning method
&lt;/h2&gt;

&lt;p&gt;I won't say the Spiral suits everyone. I had personally taught some friends and my brother this method and some of them failed. So this part of the article mentions the caveats I learned from my journey.&lt;/p&gt;

&lt;h3&gt;
  
  
  The general challenges
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You will constantly feel overwhelmed because knowledge is overloaded and it is hard to find a good mentor or guideline. Most of the time you need to go through this path alone. But if you could find a companion it would help.&lt;/li&gt;
&lt;li&gt;You may find out from time to time there are some errors or pitfalls that keep showing up. That will be the point you need to examine the correctness of your learning. Try to slow down a bit, learn this particular knowledge as a whole and correctly understand it. Try to leverage the Feynman method at this point.&lt;/li&gt;
&lt;li&gt;It is constantly struggling and you need to make yourself enjoy it. Focus on the joy when you crack the hard question will help and don't forget to celebrate the small victory. You could buy yourself a drink or have a great dinner with your loved one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drive by pure curiosity
&lt;/h3&gt;

&lt;p&gt;The Spiral is all about goal setting and achieving. But there is another great force that will push people to learn, curiosity. During my implementation and carving of this methodology, I find out it's hard to learn something when I can't imagine what is the goal I want to achieve with it. &lt;/p&gt;

&lt;p&gt;For example, I am learning Scheme language with the help of The Little Schemer. Great book, no doubt. But I feel demotivated very hard during the learning process. Because I can't imagine what I can achieve with this language. &lt;/p&gt;

&lt;p&gt;The Spiral is like a double-edged sword. You need to balance it with another method to have a healthy mindset toward knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  When the goal is too big but you really want to finish it
&lt;/h3&gt;

&lt;p&gt;There is another disadvantage of theSpiral. Once you come to a point where what you learn from a simple goal can't sufficiently fulfill your curiosity. But if you want to leap over the deeper water, you may find out that the scale of the goal may be too big to accomplish. &lt;/p&gt;

&lt;p&gt;At this moment I would like to congratulate you first. You have to walk through all the harshness of this learning method and come to the sweet spot. &lt;/p&gt;

&lt;p&gt;I would say the end game of learning is to come up with something new, something nobody had thought about. Once you enter this stage you will discover the skyline becomes clearer. You could find somebody to build with you or just cut the goal into small pieces and finish them one by one. Either way, you are approaching something extraordinary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra passion overcomes ordinary
&lt;/h2&gt;

&lt;p&gt;Boiled to the bottom line, passion is the key to learning new things. I would like to write another post about how to hack your passion but I will share my viewpoint here first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treat your passion like a campfire, you need to burn something to sustain the fire. In the context of human beings that will be the reward. So remember to reward yourself every time you accomplish something no matter the size of it. But there is some research that indicates inner reward is better than outer reward, so it will be better if you could find joy from inside along the way.&lt;/li&gt;
&lt;li&gt;Try to find a concept that people around you tend to not agree with. Dig deeper and find the reason people overlook the concept then try to prove them wrong. I find this very useful to sustain my passion. &lt;/li&gt;
&lt;li&gt;Know yourself better with some tools. For example, you could take the VIA Character Strengths Survey to know your strength and what gives you purpose. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Spiral is like a self-generated/motivated wheel. Once you finish a goal and move on to the next one. You will find yourself having the confidence and the endurance to test the limit and gain more power from it. &lt;/p&gt;

&lt;p&gt;This method greatly changes my life and makes me a better learner toward this fast-paced world. I hope you can find some point that you can utilize and practice in your daily life. May the force be with you.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://roadmap.sh/"&gt;Developer Roadmap&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>learn</category>
      <category>knowledge</category>
    </item>
    <item>
      <title>Don’t over emphasize the importance of the react folder structure, focus on the philosophies beyond the folder structure</title>
      <dc:creator>Summerbud</dc:creator>
      <pubDate>Tue, 06 Sep 2022 03:51:11 +0000</pubDate>
      <link>https://dev.to/summerbud/dont-over-emphasize-the-importance-of-the-react-folder-structure-focus-on-the-philosophies-beyond-the-folder-structure-o48</link>
      <guid>https://dev.to/summerbud/dont-over-emphasize-the-importance-of-the-react-folder-structure-focus-on-the-philosophies-beyond-the-folder-structure-o48</guid>
      <description>&lt;p&gt;Managing proper react component folder structure is hard, it needs to be flexible, extendable, and most important of all, it needs to make naming a component as easy as possible.&lt;/p&gt;

&lt;p&gt;I adapted several react folder structures across 3 different repo this year, what I discovered is not a steady structure but some philosophies behind the structure. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structure should be consistent and the principle should be documented.&lt;/li&gt;
&lt;li&gt;Component's materials should stay near to the component itself.&lt;/li&gt;
&lt;li&gt;It's a good practice to export your component to the outer responsibility layer.&lt;/li&gt;
&lt;li&gt;It's a good practice to limit the length of the filename to 3. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would like to introduce the different structures I used first and then share the context of these philosophies with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/EiffelFly/pnyx"&gt;Pnyx&lt;/a&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
|-- public
|-- src
|   |-- config
|   |-- lib
|   |-- locales
|   |-- pages
|   |-- shared-hooks
|   |-- static
|   |   `-- images
|   |-- types
|   |-- utilities
|   |-- components
|   |   |-- buttons
|   |   |-- layouts
|   |   |-- links
|   |   |-- Footer.tsx
|   |   |-- Header.tsx
|   |   `-- ...etc
|   |-- App.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple project and most of its pages are static. As a first-time React user, I learned this structure from videos on Youtube. The whole repo was maintained in a flat manner. There was no nested folder with a duplicate name like &lt;code&gt;/components/PageHeader/PageHeader.tsx&lt;/code&gt; and I didn’t export any component to its outer layer. Because the codebase is relatively small I didn’t observe much problems during writing this project.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/instill-ai/instill.tech"&gt;instill.tech@0.1.6&lt;/a&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
|-- .github
|-- .husky
|-- .storybook
|-- .__mocks__
|-- .__tests__
|   |-- ui
|   |   |-- Headline.test.tsx
|-- contexts
|-- .hooks
|-- .lib
|-- markdown
|-- pages
|-- public
|-- stories
|   |-- ui
|   |   |-- Headline.stories.tsx
|-- styles
|-- components
|   |-- forms
|   |-- layouts
|   |-- ui
|   |   |-- blocks
|   |   |-- buttons
|   |   |-- groups
|   |   |-- links
|   |   |-- Headline.tsx
|   |   |-- SubHeadline.tsx
|   |   `-- ...etc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After further exploring and digging deeper, this project has several changes compared to Pnyx. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It adapted storybook and stored stories in a different folder,&lt;/li&gt;
&lt;li&gt;It had the test suites and stored the test files in a different folder,&lt;/li&gt;
&lt;li&gt;Component folder had categories like &lt;code&gt;form&lt;/code&gt; and &lt;code&gt;layouts&lt;/code&gt;, it grouped the same components together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This structure was experimental and I had not formed a steady opinion of React folder structure at that moment. But back then the idea of separating component material(like stories and tests) into different folders caused a lot of trouble. When I refactored some minor changes beneath the component folder, I needed to adjust the &lt;code&gt;/tests&lt;/code&gt; and &lt;code&gt;/stories&lt;/code&gt; folder too. These folders became inconsistent over time.&lt;/p&gt;

&lt;p&gt;Besides, those components were not exported to the outer level, so it was hard to digest them.  Every import of a component will occupy a line of code and it looks messy.&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;import&lt;/span&gt; &lt;span class="nx"&gt;ClientBlock&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/components/ui/blocks/ClientBlock&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;Headline&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/components/ui/Headline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Image some Next.js page component that have 50 to 75 lines of this kind of import and it is very chaotic.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/instill-ai/instill.tech"&gt;instill-ai/instill.tech@0.13.0&lt;/a&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
|-- .github
|-- .husky
|-- .storybook
|-- docs
|-- public
|-- release-please
|-- src
|   |-- contexts
|   |-- hooks
|   |-- styles
|   |-- types
|   |-- lib
|   |-- pages
|   |-- components
|   |   |-- about
|   |   |   |-- OurMembers
|   |   |   |   |-- OurMembers.tsx
|   |   |   |   |-- MemberIntro.tsx
|   |   |   |   `-- index.ts
|   |   |   `-- index.ts
|   |   |-- career
|   |   |-- docs
|   |   |-- landing
|   |   |-- policy
|   |   |-- ui
|   |   |   |-- Nav
|   |   |   |   |-- Nav.tsx
|   |   |   |   |-- AboutPageLink.tsx
|   |   |   |   `-- index.ts
|   |   |   |-- PageHead.tsx
|   |   |   `-- index.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the current version of my favored folder structure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component's materials are near the component itself.&lt;/li&gt;
&lt;li&gt;General purpose components will stay in the &lt;code&gt;components/ui&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;Non-general-purpose components will stay in the parent's component folder.

&lt;ul&gt;
&lt;li&gt;For example, &lt;code&gt;MemberIntro.tsx&lt;/code&gt; is a disposable component that will only be used by its parent &lt;code&gt;OurMember.tsx&lt;/code&gt; so it should stay in the 
&lt;code&gt;components/about/OurMember&lt;/code&gt; folder not in the &lt;code&gt;components/ui&lt;/code&gt; folder.
This act can make naming components much easier compared to treating every component as general purpose components and putting them into the &lt;code&gt;components/ui&lt;/code&gt; folder.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;All components are exported to its outermost responsibility folder. For example, &lt;code&gt;Nav&lt;/code&gt; will be exported to the &lt;code&gt;/components/ui&lt;/code&gt; folder and the &lt;code&gt;OurMember&lt;/code&gt; component will be exported to the &lt;code&gt;/components/about&lt;/code&gt;. In this way, the import code will look cleaner.&lt;/li&gt;
&lt;li&gt;Under the &lt;code&gt;/components&lt;/code&gt; folder, the components are categorized by their responsibility not their functionality. For example, there won’t be a category like &lt;code&gt;/block&lt;/code&gt; and &lt;code&gt;/section&lt;/code&gt; but &lt;code&gt;/onboarding&lt;/code&gt;, &lt;code&gt;/career&lt;/code&gt;, and &lt;code&gt;/about&lt;/code&gt;. In this way, we could limit general-purpose components in just one place and other components are treated like disposable components under their responsibility folder.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;Nav&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PageHead&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="s2"&gt;@/components/ui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OurMember&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="s2"&gt;@components/about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a href="https://github.com/instill-ai/design-system"&gt;instill-ai/design-system&lt;/a&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
|-- .github
|-- .husky
|-- .storybook
|-- public
|-- src
|   |-- hooks
|   |-- styles
|   |-- types
|   |-- utils
|   |-- index.ts
|   |-- ui
|   |   |-- index.ts
|   |   |-- Buttons
|   |   |   |-- ButtonBase
|   |   |   |   |-- ButtonBase.tsx
|   |   |   |   |-- ButtonBase.stories.tsx
|   |   |   |   `-- index.ts
|   |   |   |-- SolidButton
|   |   |   |   |-- SolidButton.tsx
|   |   |   |   |-- SolidButton.stories.tsx
|   |   |   |   `-- index.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This project is a special case when it comes to folder structure. It uses the structure I called "Base and Export" two-level component design. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Base-level components: The foundation of every type of component.

&lt;ul&gt;
&lt;li&gt;  Base level components won't be exported to be used by exterior consumers.&lt;/li&gt;
&lt;li&gt;  Base components will define all the props and functions that the component needs.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;  Exported-level components: Inherits all the props from a base component but may have a predefined style, meant to be used by the consumers.

&lt;ul&gt;
&lt;li&gt;  Exported-level components will be consumed with the limited props like onChangeInput, borderStyle, fontFamiliy, or other functional props.&lt;/li&gt;
&lt;li&gt;  We will limit as few style props an exported component needs as possible, to control the visual style of the component.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a unique way to organize our design system. Overall it brings some fresh air into our projects and serves our needs well. &lt;/p&gt;

&lt;h2&gt;
  
  
  Philosophies
&lt;/h2&gt;

&lt;p&gt;During this journey, what I learned the most is "There is no one fit for all folder structures". If you encounter someone claiming that, it will be better to leave them there and move on. The reason is quite simple: The technology we use is drastically changing. For example, Next.js has a new layout RFC that will change our folder structure a lot, and the adaption of storybook changes the way we thought about components too. I foresee that we will have more frameworks and libraries like these.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structure should be consistent and the principle should be documented.
&lt;/h3&gt;

&lt;p&gt;This is my first point. The codebase is not a single person's playground but a collective intelligence that the maintainers had to agree on. No matter what you adopt, you should make the structure as clear and consistent as possible. In this way, other maintainers will have a clear path to look into your project and once you need to refactor the whole repo to adopt a new tech it will be easier to migrate a consistent structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Component's materials should stay near to the component itself.
&lt;/h3&gt;

&lt;p&gt;This is the mistake I made at &lt;code&gt;instill-ai/instill.tech@0.6.1&lt;/code&gt; that caused lots of trouble and inconsistency when I tried to refactor its structure. Overall I think we should put our component's materials close to the component itself. Not only to reduce the overhead of refactoring but also to help others find your component. In this way we could help the maintainers to name their components easier too, because they don’t need to come up with a component name that can describe itself in various contexts. It only needs to describe itself under the context of its parent. &lt;/p&gt;

&lt;h3&gt;
  
  
  It's a good practice to export your component to the outer responsibility layer.
&lt;/h3&gt;

&lt;p&gt;When your file has 30 to 50 imports, it will be messy if the imported function or component are not exported to their outer folder. Imagine you have 30 lines of this kind of code.&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;import&lt;/span&gt; &lt;span class="nx"&gt;ClientBlock&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/components/ui/blocks/ClientBlock&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;Headline&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/components/ui/Headline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compared to this way.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ClientBlock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Headline&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="s2"&gt;@/components/ui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I think the latter is better.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's a good practice to limit the length of the filename to 3.
&lt;/h3&gt;

&lt;p&gt;This philosophy comes from a friend of mine. Because we are using the PascalCase for the naming convention of our components, it may be hard to read when the length of words exceeds 3. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;CallToAction.tsx&lt;/code&gt; and &lt;code&gt;CallToActionBlock.tsx&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t over-emphasize the importance of a structure
&lt;/h2&gt;

&lt;p&gt;The ideal structure is changing and changing fast, and it highly relates to the technology we adapt to. I think the best method is to maintain these kinds of fundamental philosophies and adapt new tech and structure in a quick and flexible manner. &lt;/p&gt;

&lt;p&gt;&lt;sup&gt;Photo by &lt;a href="https://unsplash.com/@viktortalashuk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Viktor Talashuk&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/folder?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to solve: TailwindCSS IntelliSense can't pick up your custom utility classname</title>
      <dc:creator>Summerbud</dc:creator>
      <pubDate>Fri, 02 Sep 2022 03:06:38 +0000</pubDate>
      <link>https://dev.to/summerbud/how-to-solve-tailwindcss-intelligence-cant-pick-up-your-custom-utility-classname-1h1j</link>
      <guid>https://dev.to/summerbud/how-to-solve-tailwindcss-intelligence-cant-pick-up-your-custom-utility-classname-1h1j</guid>
      <description>&lt;p&gt;Recently, I finally solved a big issue with the TailwindCSS that bother me for a long period of time. That is TailwindCSS intelligence won't pick up the custom utility classnames using the @layer directive.&lt;/p&gt;

&lt;p&gt;When you are building the app with TailwindCSS, at some point you need to add a custom style. You could add a custom style within &lt;code&gt;tailwind.config.js&lt;/code&gt; file. In this way, TailwindCSS intelligence can correctly pick it up for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tailwind.config.js&lt;/span&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;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;colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;primary_colour_light_blue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#40A8F5&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if we want a much more complicated style? The next step comes naturally with the help of a custom utility class using the @layer directive at the CSS file we just injected into the app root.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;@layer&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s1"&gt;".text-header1"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;@apply&lt;/span&gt; &lt;span class="err"&gt;font-sans&lt;/span&gt; &lt;span class="err"&gt;text-[32px]&lt;/span&gt; &lt;span class="err"&gt;font-medium&lt;/span&gt; &lt;span class="err"&gt;leading-[42px];&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="s1"&gt;".custom-shadow"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="py"&gt;boxshadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"0px 0px 0px 3px rgba(64, 168, 245, 0.2)"&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;And here comes the problem, although this can work, TailwindCSS will not purge this class name and correctly digest it as CSS, the TailwindCSS intelligence can't pick up this class. So when you hover the class itself &lt;code&gt;text-header1&lt;/code&gt; on your IDE, there won't be any style information popping up(issue) &lt;sup id="fnref2"&gt;2&lt;/sup&gt; The reason is quite simple, TailwindCSS intelligence doesn't read through your file and find the injection file, it only read the &lt;code&gt;tailwind.config.js&lt;/code&gt; at the root of the project.&lt;/p&gt;

&lt;p&gt;So how can I add a custom style that can be picked up by TailwindCSS intelligence? Here comes Tailwind plugin for rescue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind CSS plugin
&lt;/h2&gt;

&lt;p&gt;You can use the TailwindCSS plugin to add any custom style you want and intelligence will pick it up for you. The way to add a custom utility class is quite simple.&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;plugins&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;addUtilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addComponents&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;addUtilities&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.custom-shadow&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;boxShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0px 0px 0px 3px rgba(64, 168, 245, 0.2)&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could also add the &lt;code&gt;@apply&lt;/code&gt; directive in plugin&lt;sup id="fnref3"&gt;3&lt;/sup&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;plugins&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;addUtilities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addComponents&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;addUtilities&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.test-color-red&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@apply text-red-900&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After setting up your plugin, the intelligence can now correctly pick up the custom style, but this is not an ideal solution but only a comfy workaround. I am looking forward to one day, the TaiwlindCSS have native support for this issue.&lt;/p&gt;

&lt;p&gt;Here is some working example: &lt;a href="https://github.com/instill-ai/console/blob/d57d2651a450c3d712a95c9d2c6464ee204bba30/tailwind.config.js#L77"&gt;instill-ai/console&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;Photo by &lt;a href="https://unsplash.com/@jenegallery?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Jené Stephaniuk&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/palette?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://tailwindcss.com/docs/guides/nextjs"&gt;TailwindCSS installation example - Next.js&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://github.com/tailwindlabs/tailwindcss-intellisense/issues/227"&gt;Tailwind IntelliSense does not list my custom class in @layer Utilities #227&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://github.com/tailwindlabs/tailwindcss/discussions/2049"&gt;JS API for using &lt;code&gt;@apply&lt;/code&gt; in Plugins&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>tailwindcss</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>css</category>
    </item>
    <item>
      <title>Build the theme toggle button with Astrojs and tailwindCSS</title>
      <dc:creator>Summerbud</dc:creator>
      <pubDate>Thu, 01 Sep 2022 05:12:56 +0000</pubDate>
      <link>https://dev.to/summerbud/build-the-theme-toggle-button-with-astrojs-and-tailwindcss-mmp</link>
      <guid>https://dev.to/summerbud/build-the-theme-toggle-button-with-astrojs-and-tailwindcss-mmp</guid>
      <description>&lt;p&gt;What Astro had brought to us is a world without Javascript as default. They use this philosophy to reduce bundle size and speed up the first paint of the browser, but it also brings lots of new challenges to the developer, such as toggling the dark theme button on the blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  With Next.js
&lt;/h2&gt;

&lt;p&gt;I wrote a ThemeContext to toggle dark mode in my blog built on top of Next.js. The ThemeContext will add &lt;code&gt;class="dark"&lt;/code&gt; to the root HTML upon mounting the app. In this way, TailwindCSS can recognize what is the current theme.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The code I am using in my blog built with Next.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createContext&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="s2"&gt;react&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;defaultState&lt;/span&gt; &lt;span class="o"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;toggleDark&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="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="na"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//dev-to-uploads.s3.amazonaws.com/uploads/articles/q0bxxcx8cz7eea9bmoem.jpg)&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ThemeContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaultState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ThemeProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;initialTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="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;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;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&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;rawSetTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawTheme&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;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&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;isDark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawTheme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isDark&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rawTheme&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;initialTheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;rawSetTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialTheme&lt;/span&gt;&lt;span class="p"&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="nx"&gt;rawSetTheme&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="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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ThemeContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&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="nx"&gt;setTheme&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;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ThemeContext.Provider&lt;/span&gt;&lt;span class="err"&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;h2&gt;
  
  
  With Astro.js
&lt;/h2&gt;

&lt;p&gt;But If we want to remain the usage of Context API, we may need to write something like the 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="c1"&gt;// mainPage.astro&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ContextWrapperedComponent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./ContextWrapperedComponent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ContextWrapperedComponent&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;


&lt;span class="c1"&gt;// ContextWrapperedComponent&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ContextWrapperedComponent&lt;/span&gt; &lt;span class="o"&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="c1"&gt;// logic for context and components&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="c1"&gt;// bunch of components that rely on context&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&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;Based on the idea of Astro.js - Partial Hydration&lt;sup id="fnref2"&gt;2&lt;/sup&gt;, we need to label the whole &lt;code&gt;ContextWrapperedComponent&lt;/code&gt; as &lt;code&gt;client:load&lt;/code&gt; or &lt;code&gt;client:only&lt;/code&gt; to make it hydrated and inject it to HTML as Javascript. But in this way, there are lots of not necessary scripts that got injected and increase the overall bundle size which is not what we want.&lt;/p&gt;

&lt;p&gt;So I decided to switch my storage of dark mode state from Context to localStorage. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upon the first-time mounting, I will access the key's(blog-theme) value under localStorage and determine the page's theme according to this value.&lt;/li&gt;
&lt;li&gt;Once the user toggles the theme I will reset the theme of the page and update the value under localStorage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the first glance, this implementation looks straightforward, but it still has lots of caveats.&lt;/p&gt;




&lt;h2&gt;
  
  
  Caveat 1 - Blinking pages
&lt;/h2&gt;

&lt;p&gt;The rendering process of Astro is first sending the HTML and CSS to the client, mounting the hydrated template string, and listening to the event to inject the script. This behavior may lead to some unwanted results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This code will update the page's theme upon the finsish &lt;/span&gt;
&lt;span class="c1"&gt;// of the first time painting, but that is not what we want.&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;let&lt;/span&gt; &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;theme&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="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&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="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="k"&gt;else&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="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&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="s2"&gt;light&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="nx"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="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;For example, if your default theme is the dark mode but what is stored in localStorage is the light mode. The HTML will first display dark mode, once the client-side is mounted, the event triggers hydrated script injection, your page will be updated as the light mode and this process will keep operating every time the user change pages. As the result, your pages will keep blinking.&lt;/p&gt;

&lt;p&gt;To solve the blinking issue, we have to update the theme before the first painting of the browser.&lt;/p&gt;

&lt;p&gt;Thankfully, Astro.js have already looked after our back, in their API you could make a script sent to the client with HTML and CSS as-is with the special attribute &lt;code&gt;is:inline&lt;/code&gt;&lt;sup id="fnref3"&gt;3&lt;/sup&gt;. Astro won't hydrate this script and practice any kind of optimization. To seriously put, this is not welcome under the philosophy of Astro but it is necessary for our application. Besides that, Astro.js's official document has also adopted this approach.&lt;sup id="fnref4"&gt;4&lt;/sup&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="c1"&gt;// With this code, we can update the page's theme before the first-time painting.&lt;/span&gt;
&lt;span class="c1"&gt;// You should put the code into &amp;lt;script is:inline&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html&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;theme&lt;/span&gt; &lt;span class="o"&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(prefers-color-scheme: dark)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;matches&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;

&lt;span class="k"&gt;if&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="s2"&gt;light&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="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Caveat 2 - Transition of the toggle them button
&lt;/h2&gt;

&lt;p&gt;Originally, my toggle dark mode button is under dark mode it will display the sun icon and display the moon icon under the light mode. The issue of this design is close to caveat 1, it will have the blinking issue. At my first thought, I think I can update the icon just like I did with the above solution, but later on, I discovered this didn't work and the reason is quite straightforward.&lt;/p&gt;

&lt;p&gt;Because we need the toggle button to be interactive, it needs to prefix with &lt;code&gt;client:load&lt;/code&gt; to indicate Astro.js hydrate this script. But upon the first painting, the hydrated script is not injected yet, so the &lt;code&gt;is:inline&lt;/code&gt; script can't find the target button to update the icon. The button will only show up once the first painting is finished.&lt;/p&gt;

&lt;p&gt;To solve this problem, I need to alter the design of the toggle button. It now displays the sun and the moon icon at the same time but has a glimmer transition to indicate which one is the current theme.&lt;/p&gt;

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

&lt;p&gt;Coding with Astro.js is a fresh journey, it has lots of potential to be discovered, I will keep updating what I found during the exploration.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://tailwindcss.com/docs/dark-mode"&gt;TailwindCSS - dark mode&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://docs.astro.build/en/core-concepts/partial-hydration/"&gt;Astro - Partial Hydration&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://docs.astro.build/en/reference/directives-reference/#isinline"&gt;Astro - is:inline&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://github.com/withastro/docs/blob/b268cae6af9887060f01d31b213c312fe1ce2c3c/src/layouts/MainLayout.astro#L114"&gt;withAstro - docs - GItHub&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tailwindcss</category>
      <category>astro</category>
    </item>
    <item>
      <title>Why is the documentation of tech products so hard to use? (In the user’s point of view)</title>
      <dc:creator>Summerbud</dc:creator>
      <pubDate>Wed, 31 Aug 2022 02:00:34 +0000</pubDate>
      <link>https://dev.to/summerbud/why-is-the-documentation-of-tech-products-so-hard-to-use-in-the-users-point-of-view-12im</link>
      <guid>https://dev.to/summerbud/why-is-the-documentation-of-tech-products-so-hard-to-use-in-the-users-point-of-view-12im</guid>
      <description>&lt;p&gt;Nowadays, our documentation of developer tools is hard to use and we tend to find the solution on other sources such as Youtube, GitHub issues, or blog posts. Its content may easily fall behind or the key point has not been mentioned at all. I consider this issue an emergent problem that we should toggle as soon as possible, and I think two major problems needed to be solved first. (In the user’s point of view)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation is too hard to contribute to

&lt;ul&gt;
&lt;li&gt;Documentation easily becomes out of date. People tend to write a blog post rather than contribute to a document because it requires a lot of extra effort without the corresponding incentives.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The context is clustered

&lt;ul&gt;
&lt;li&gt;Documentation itself hardly connects to other resources. The context that makes good documentation is clustered.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, I would like to introduce two concepts around documentation that I think are problematic and then further expand it to 3 thinking points toward solving these two issues.&lt;/p&gt;

&lt;p&gt;First of all, please recall some documentation you have visited recently and join me with the explanation below. I would like to take Next.js for example which is currently my most visited documentation. Let's start with the "Getting Started" page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/getting-started"&gt;https://nextjs.org/docs/getting-started&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nextjs's documentation has a structure that is popular through various products, let me call this structure "Isolated listing structure". The reason I call them isolated is that they are mainly maintained by the people behind Next.js(although they are open-sourced).&lt;/p&gt;

&lt;p&gt;Furthermore, I would like to conclude the mindset behind this documentation as "Owner's bullet-point".&lt;/p&gt;

&lt;h2&gt;
  
  
  Owner's bullet-point
&lt;/h2&gt;

&lt;p&gt;The reason I call it "Owner's bullet point" is the process of generating documentation is usually accomplished by the product owner. At the start of a product, no one has an understanding better than the owner. The owners are the best person to maintain proper documentation for their consumers and maintainers.&lt;/p&gt;

&lt;p&gt;But after a while, the product receives a lot of love and begins to build up the community. It becomes harder and harder to follow up on corner cases and bugs. The owners have to catch up with new commits and solve issues daily, on the other hand, they have to explain new designs, and caveats and provide information for newcomers to overcome these problems. The loading is rising high drastically.&lt;/p&gt;

&lt;p&gt;Not every product can catch up, the content begins to fall behind, and some solutions may exist in others' blog posts, StackOverflow's answers, GitHub issues, or discussions. A user needs to connect these solutions with search engines, sometimes the solution on documentation is even wrong. &lt;/p&gt;

&lt;h2&gt;
  
  
  Isolated listing structure
&lt;/h2&gt;

&lt;p&gt;The isolated listing structure is very common, in e-commerce, in general-purpose websites, on your phone, and in the documentation. It's everywhere. &lt;/p&gt;

&lt;p&gt;The structure is mostly arbitrary and opinionated coming from the mindset of "Owner's bullet-point", due to the owner having to first come up with the tree of the structure, in an order that they think is most suitable for their client. &lt;/p&gt;

&lt;p&gt;The isolated listing structure is a double-edged sword: To be clear, I am not fully against this structure, it is really helpful in a general context. For example, it's good for initial exploration and if you're familiar with the document, it's quite easy for you to find the information you want.&lt;/p&gt;

&lt;p&gt;But on the other hand, it's a fixed structure, it is hard for the structure to evolve and every time the maintainer wants to add something else, it's hard to find an appropriate place if the owner didn't think it well from the beginning. Besides that, users have no other choice but to explore your documentation. They have only one route and it's not enough.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Iy3dJ_QS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8vi2bdc63hdzy99ttl0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Iy3dJ_QS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8vi2bdc63hdzy99ttl0.png" alt="Force-directed graph" width="880" height="656"&gt;&lt;/a&gt;&lt;br&gt;
&lt;sup&gt;Martin Grandjean, CC BY-SA 3.0 &lt;a href="https://creativecommons.org/licenses/by-sa/3.0"&gt;https://creativecommons.org/licenses/by-sa/3.0&lt;/a&gt;, via Wikimedia Commons&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Imagine a dynamic structure, like a force-directed graph (Something like Obsidian can accomplish or a tree diagram, you could find many examples right in their publish section&lt;sup id="fnref1"&gt;1&lt;/sup&gt;). I am not saying force-directed graphs or tree diagrams are better than isolated listing structures. What I want is to encourage everyone not to think in a "listing way" but in a more "dynamic way", so that people can choose whatever suits their needs best. They can use tree diagrams to explore the structure and use the force-directed graph to acknowledge the connection between topics. Or we could come up with a new structure from the ground up which can solve some of these problems.&lt;/p&gt;

&lt;p&gt;In combination, the mindset and the structure lead to the problem I think is problematic, listed below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation is too hard to contribute to
&lt;/h2&gt;

&lt;p&gt;In combination with the arbitrary mindset of "Owner bullet's point" and the "Isolated listing structure", what we have is that current documentation can only be properly maintained by the owner, there is no other easy way to contribute to the documentation. The problem is twofold.&lt;/p&gt;

&lt;p&gt;First, based on the "Owner's bullet point" mindset, the author doesn't want some inexperienced person to mess up your documentation and it's quite hard to match the tone of documentation if you don't spend much time staying aligned with your maintainers. &lt;/p&gt;

&lt;p&gt;Second, your user doesn't have any incentive to contribute to the documentation, people love your product but if they don't get any credit within the construction of the document or some ownership toward some page of documentation, there is no incentive.&lt;/p&gt;

&lt;p&gt;You may argue that they can post an issue and explain their suggestion but the problem remains. The feeling of contributing is very simultaneous, it is just like the feeling of buying something. Imagine a parallel world, in here, if you want to buy something, you need to write down 200 words to describe why you want to buy this stuff and what is the benefit the community. &lt;/p&gt;

&lt;p&gt;There is no need to gate people from contributing documentation with bureaucracy. We need to come up with another structure that can remain the authority of the documentation and benefit the simultaneous contribution at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The context is clustered
&lt;/h2&gt;

&lt;p&gt;The material of good documentation is not just the documentation itself, but the corresponding discussions, issues, related blog posts, and videos. I will call these materials "Contexts".&lt;/p&gt;

&lt;p&gt;Until recently we tended to store these contexts distributedly. A regular open-source project stores their discussion in GitHub discussion or a discourse forum, their use cases in product websites, issues right in the GitHub issues, and a self-hosting documentation website elsewhere. Besides that, there is a lot of community-generated content on YouTube and personal blog posts.&lt;/p&gt;

&lt;p&gt;In reality, the context of a product becomes clustered. They won't have inter-connection with each other. There may be a discussion about the solution to the specific bug, but there is no reverse link on the documentation section that has mentioned this solution. Sadly we have to deal with the reality that the bi-directional link is not what the current internet is capable of.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clustered context is volatile
&lt;/h3&gt;

&lt;p&gt;Imagine a situation where you drop in the product's documentation and quickly find out that the documentation doesn't provide a solution, later the day you find a workable solution on someone else's blog post. &lt;/p&gt;

&lt;p&gt;At this moment, with the current documentation, there is no other way to remind other developers that they can solve the same problem with this method described by the blog post, you can't add a reference to the documentation. What you can do is open an issue(if it's open-source) or open a discussion on a forum to remind people about this solution which will soon be flooded by other content.&lt;/p&gt;

&lt;p&gt;Every useful solution in any context needs to be resistant to the flood of information we have right now. They have to join this battleground with no anchor point besides search engines. &lt;/p&gt;

&lt;p&gt;The awesome list &lt;sup id="fnref2"&gt;2&lt;/sup&gt; is a good idea, they provide a way to let good contents stay, but they have the same issue with the "Isolated listing structure" and the "Owner's bullet-point" mindset.&lt;/p&gt;

&lt;h2&gt;
  
  
  If the situation remains as is…
&lt;/h2&gt;

&lt;p&gt;The immediate consequence of these issues will be the “Documentation” will deem to decay over time. You could take a look at some tech giant’s documentation including Amazon web services documentation or Google cloud documentation, they are all overwhelming and hard to read. &lt;/p&gt;

&lt;p&gt;The worst feeling is you are stucking on a specific problem that you can’t find in the documentation and anywhere else. We will be facing these kinds of situations more if the structure of our documentation can not align with the scope of the product we use.&lt;/p&gt;

&lt;p&gt;It may seem overwhelming to come up with a new kind of structure to overcome these issues in the first place. But look closely, we could separate the overall problems into 3 questions to ask ourselves.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to encourage users to contribute to the documentation and not interfere with the general purpose of the document? Can we have a more interactive experience for documentation? Can a user directly contribute to the documentation without leaving the page?&lt;/li&gt;
&lt;li&gt;How to gather context together for a better search experience that does not rely on exterior search solutions and brings up inter-connectivity of every context along the way?&lt;/li&gt;
&lt;li&gt;How to experiment with a new structure that may benefit user experience and iterate it over time, or even let users choose it freely?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want to progressively tackle these issues. If you are interested in or come up with something interesting about this topic, I'd like to hear about it. I would answer any email (&lt;a href="mailto:summerbud.chiu@gmail.com"&gt;summerbud.chiu@gmail.com&lt;/a&gt;) about this topic, you could also find me on Twitter (&lt;a href="https://twitter.com/EiffelFly"&gt;https://twitter.com/EiffelFly&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;Cover Photo by &lt;a href="https://unsplash.com/es/@wesleyphotography?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Wesley Tingey&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/document?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://obsidian.md/publish"&gt;Obsidian publish page&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://github.com/sindresorhus/awesome"&gt;sindresorhus/awesome&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>documentation</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
