<?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: Bruno Sabot</title>
    <description>The latest articles on DEV Community by Bruno Sabot (@brunosabot).</description>
    <link>https://dev.to/brunosabot</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%2F134324%2F3367fc6a-dc12-41c8-8720-8fd6fc32c8fb.jpg</url>
      <title>DEV Community: Bruno Sabot</title>
      <link>https://dev.to/brunosabot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brunosabot"/>
    <language>en</language>
    <item>
      <title>Crafting My Perfect Home Assistant Dashboard</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Sat, 13 Jul 2024 06:20:00 +0000</pubDate>
      <link>https://dev.to/brunosabot/crafting-my-perfect-home-assistant-dashboard-5ejc</link>
      <guid>https://dev.to/brunosabot/crafting-my-perfect-home-assistant-dashboard-5ejc</guid>
      <description>&lt;p&gt;Inspired by the beautiful and functional dashboards I've seen online, I embarked on a journey to personalize my own Home Assistant experience. This post dives into the plugins I used, my customization choices, and a final glimpse of the finished product.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introducing the Plugin Powerhouse&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Several plugins played a pivotal role in building my dream dashboard, each offering unique functionalities that elevated the user experience.&lt;/p&gt;

&lt;p&gt;I meticulously handpicked a select few from the vast plugin library, prioritizing consistency and cohesiveness around the versatile &lt;a href="https://github.com/Clooos/Bubble-Card" rel="noopener noreferrer"&gt;Bubble Card&lt;/a&gt;, to craft this personalized experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bubble Card
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fbubble-card.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fbubble-card.png" alt="bubble-card.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I recently stumbled upon the &lt;a href="https://github.com/Clooos/Bubble" rel="noopener noreferrer"&gt;Bubble Card&lt;/a&gt; plugin, and let me tell you, it was a game-changer!&lt;/p&gt;

&lt;p&gt;This new plugin quickly became the star of my dashboard makeover. Its cool, rounded design elements totally revamped the whole look and feel, making everything smooth and visually appealing.&lt;/p&gt;

&lt;p&gt;To take things to the next level, I also started using the &lt;a href="https://github.com/Clooos/Bubble" rel="noopener noreferrer"&gt;Bubble&lt;/a&gt; theme, created by the same awesome creator.&lt;/p&gt;

&lt;p&gt;Together, they work like magic, creating a consistent and stunning dashboard that's more than just the sum of its parts.&lt;/p&gt;

&lt;p&gt;It's like my dashboard went from info central to an interactive and visually engaging masterpiece!&lt;/p&gt;

&lt;h3&gt;
  
  
  Card Mod
&lt;/h3&gt;

&lt;p&gt;Anyone who's ever tinkered with their dashboard to make it look snazzy probably knows about Card Mod – it's kind of a no-brainer.&lt;/p&gt;

&lt;p&gt;This awesome plugin lets you, well, mod your cards by taking styles from other cards. Need to change the background color, for example? Card Mod is your new best friend.&lt;/p&gt;

&lt;p&gt;It helps if you know a little CSS, but even without it, you can do some pretty cool stuff and make your dashboard look way less, well, boring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decluttering Card
&lt;/h3&gt;

&lt;p&gt;Card Mod is so cool, I went a little overboard and ended up customizing practically everything! But all those tweaks scattered throughout my config started to feel like a bit of a pain to manage.&lt;/p&gt;

&lt;p&gt;That's when I discovered the magic of &lt;a href="https://github.com/custom-cards/decluttering-card" rel="noopener noreferrer"&gt;Decluttering Card&lt;/a&gt;. This plugin lets me create card templates that I can reuse everywhere, shrinking my configuration file down to a much more manageable size.&lt;/p&gt;

&lt;p&gt;Now, I can make a change in one place, and it ripples through my entire dashboard – a total win!&lt;/p&gt;

&lt;h3&gt;
  
  
  Config Template Card
&lt;/h3&gt;

&lt;p&gt;Decluttering Card definitely helps with the templating issue, but it can't handle everything, especially when things get a little more dynamic.&lt;/p&gt;

&lt;p&gt;Thankfully, the &lt;a href="https://github.com/iantrich/config-template-card" rel="noopener noreferrer"&gt;Config Template Card&lt;/a&gt; swoops in to save the day! This plugin lets you use JavaScript to make pretty much any property of your card dynamic, putting the finishing touches on your template mastery.&lt;/p&gt;

&lt;p&gt;It's like the final ingredient in the recipe for perfect dashboard customization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lovelace Layout Card
&lt;/h3&gt;

&lt;p&gt;Now I have the power to create the perfect cards, I just needed the final touch: a stellar layout.&lt;/p&gt;

&lt;p&gt;Thankfully, &lt;a href="https://github.com/thomasloven/lovelace-layout-card" rel="noopener noreferrer"&gt;Lovelace Layout Card&lt;/a&gt; swoops in to save the day! This plugin allows me to craft a perfectly responsive dashboard that flawlessly adapts to both my desktop and mobile, ensuring a seamless Home Assistant experience no matter the device I'm using.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Bringing it to Life: My Customization Journey&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In the following sections, I'll delve into the specifics of how I utilized these plugins to realize my desired dashboard functionalities.&lt;/p&gt;

&lt;p&gt;I'll provide a detailed explanation for each, empowering you to replicate or adapt these approaches to craft your own personalized Home Assistant experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alarm
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Falarm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Falarm.png" alt="alarm.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keeping tabs on my home alarm system's status is crucial. To achieve this, I needed a clear and immediate visual indicator right on my dashboard.&lt;/p&gt;

&lt;p&gt;The ideal card would dynamically change its background color based on the alarm's state: green for disabled, orange for armed, and red for triggered. Luckily, the style customization of Bubble Card allowed me to create this functionality perfectly.&lt;/p&gt;

&lt;p&gt;Now, with just a glance, I can see the alarm's status and take appropriate action if needed.&lt;/p&gt;

&lt;p&gt;Here is the decluttering-card template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom:bubble-card&lt;/span&gt;
  &lt;span class="na"&gt;card_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;button&lt;/span&gt;
  &lt;span class="na"&gt;button_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;state&lt;/span&gt;
  &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[name]]'&lt;/span&gt;
  &lt;span class="na"&gt;show_state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mdi:alarm-light&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[columns]]'&lt;/span&gt;
  &lt;span class="na"&gt;card_layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;large&lt;/span&gt;
  &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;.bubble-button-card-container {&lt;/span&gt;
      &lt;span class="s"&gt;background-color: ${state === 'disarmed' ? 'var(--success-color)' : state === 'triggered' ? 'var(--error-color)' : 'var(--warning-color)'};&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;
    &lt;span class="s"&gt;.bubble-icon {&lt;/span&gt;
      &lt;span class="s"&gt;color: ${state === 'disarmed' ? 'var(--success-color)' : state === 'triggered' ? 'var(--error-color)' : 'var(--warning-color)'} !important;&lt;/span&gt;
      &lt;span class="s"&gt;opacity: 0.6 !important;&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  AQI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Faqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Faqi.png" alt="aqi.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Monitoring air quality is a top priority for me, as I'm sensitive to pollution.&lt;/p&gt;

&lt;p&gt;Having this data readily available on my dashboard helps me maintain my health, not only through automations with my air purifier but also by allowing me to check the current pollution level at a glance.&lt;/p&gt;

&lt;p&gt;I envisioned a card that would display the Air Quality Index (AQI) using a color-coded system: green for good air quality (AQI below 50), orange for moderate air quality (AQI below 100), and red for poor air quality (AQI above 100).&lt;/p&gt;

&lt;p&gt;This way, I could easily understand the air quality and take appropriate actions, if necessary.&lt;/p&gt;

&lt;p&gt;Here is the decluttering-card template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
&lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;custom:bubble-card'&lt;/span&gt;
  &lt;span class="na"&gt;card_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;button&lt;/span&gt;
  &lt;span class="na"&gt;button_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;state&lt;/span&gt;
  &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[name]]'&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mdi:leaf&lt;/span&gt;
  &lt;span class="na"&gt;show_state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;card_layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;large&lt;/span&gt;
  &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;.bubble-button-card-container {&lt;/span&gt;
      &lt;span class="s"&gt;background-color: ${state &amp;lt; 50 ? 'var(--success-color)' : state &amp;lt; 100 ? 'var(--warning-color)' : 'var(--error-color)'};&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;
    &lt;span class="s"&gt;.bubble-icon {&lt;/span&gt;
      &lt;span class="s"&gt;color: ${state &amp;lt; 50 ? 'var(--success-color)' : state &amp;lt; 100 ? 'var(--warning-color)' : 'var(--error-color)'} !important;&lt;/span&gt;
      &lt;span class="s"&gt;opacity: 0.6 !important;&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Covers
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fcover.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fcover.png" alt="cover.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My cover cards demanded a presentation that was both information-rich and visually clear. To achieve this, I implemented a three-pronged approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Condensed Icon Spacing &amp;amp; Favorite Position Integration:&lt;/strong&gt; I tightened the spacing between the cover icons, fostering a sense of cohesion for the linked actions they represent. Additionally, I incorporated a sub-button displaying the favorite position (set at 15% in my case) directly within the card itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Opening Percentage Display:&lt;/strong&gt; The current opening percentage is crucial information, and I eliminated the need for a separate entity card by integrating it directly into the cover card.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Color-Coded Status Recognition:&lt;/strong&gt; Finally, I implemented a color-coding system to provide instant status recognition. Blue signifies a fully open cover, while purple highlights the preferred 15% position. This visual language allows for quick comprehension and informed decision-making when interacting with my covers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This combined approach effectively streamlined the cover card format, enhancing both information accessibility and user experience.&lt;/p&gt;

&lt;p&gt;Here is the decluttering-card template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
&lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;custom:bubble-card'&lt;/span&gt;
  &lt;span class="na"&gt;card_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cover&lt;/span&gt;
  &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[name]]'&lt;/span&gt;
  &lt;span class="na"&gt;icon_open&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mdi:window-shutter-open&lt;/span&gt;
  &lt;span class="na"&gt;icon_close&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mdi:window-shutter&lt;/span&gt;
  &lt;span class="na"&gt;show_state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;card_layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;large&lt;/span&gt;
  &lt;span class="na"&gt;sub_button&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;My&lt;/span&gt;
      &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mdi:star&lt;/span&gt;
      &lt;span class="na"&gt;show_background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;tap_action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;call-service&lt;/span&gt;
        &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;button.press&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;entity_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[my]]'&lt;/span&gt;
  &lt;span class="na"&gt;card_mod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-cover-card-container {&lt;/span&gt;
        &lt;span class="s"&gt;background-color:&lt;/span&gt;
          &lt;span class="s"&gt;{% if state_attr(config.entity, 'current_position') == 15 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--state-cover-active-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% elif state_attr(config.entity, 'current_position') &amp;gt; 0 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--accent-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% else %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--background-color-2, var(--secondary-background-color))&lt;/span&gt;
          &lt;span class="s"&gt;{% endif %}&lt;/span&gt;
        &lt;span class="s"&gt;!important;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.large .bubble-buttons {&lt;/span&gt;
        &lt;span class="s"&gt;gap: 8px;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-button {&lt;/span&gt;
        &lt;span class="s"&gt;background: transparent;&lt;/span&gt;
        &lt;span class="s"&gt;width: 40px;&lt;/span&gt;
        &lt;span class="s"&gt;height: 40px;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-icon {&lt;/span&gt;
        &lt;span class="s"&gt;justify-content: center;&lt;/span&gt;
        &lt;span class="s"&gt;align-items: center;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.large .bubble-sub-button-container {&lt;/span&gt;
        &lt;span class="s"&gt;margin-right: 8px;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-sub-button {&lt;/span&gt;
        &lt;span class="s"&gt;background: transparent;&lt;/span&gt;
        &lt;span class="s"&gt;width: 40px;&lt;/span&gt;
        &lt;span class="s"&gt;height: 40px;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-state::after {&lt;/span&gt;
        &lt;span class="s"&gt;content: " - {{ state_attr(config.entity, 'current_position') }}%";&lt;/span&gt;
        &lt;span class="s"&gt;margin-left: 4px;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-icon-container {&lt;/span&gt;
        &lt;span class="s"&gt;border-color: transparent;&lt;/span&gt;
        &lt;span class="s"&gt;background-color: transparent;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-icon-container::after {&lt;/span&gt;
        &lt;span class="s"&gt;content: "";&lt;/span&gt;
        &lt;span class="s"&gt;background-color:&lt;/span&gt;
          &lt;span class="s"&gt;{% if state_attr(config.entity, 'current_position') == 15 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--state-cover-active-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% elif state_attr(config.entity, 'current_position') &amp;gt; 0 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--accent-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% else %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--background-color-2, var(--secondary-background-color))&lt;/span&gt;
          &lt;span class="s"&gt;{% endif %}&lt;/span&gt;
        &lt;span class="s"&gt;!important;&lt;/span&gt;
        &lt;span class="s"&gt;height: 25px;&lt;/span&gt;
        &lt;span class="s"&gt;width: 25px;&lt;/span&gt;
        &lt;span class="s"&gt;position: absolute;&lt;/span&gt;
        &lt;span class="s"&gt;left: 50px;&lt;/span&gt;
        &lt;span class="s"&gt;top: 25px;&lt;/span&gt;
        &lt;span class="s"&gt;-webkit-mask-image: radial-gradient(circle at top right, transparent 0, transparent 25px, black 25.5px);&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Separator
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fseparator.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fseparator.png" alt="separator.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To keep my dashboard organized, I grouped things into sections.&lt;/p&gt;

&lt;p&gt;Bubble Card's separator mode was perfect for adding section titles, but I wanted to give them a little makeover.&lt;/p&gt;

&lt;p&gt;So, I played around with some CSS to make the font lighter and bigger, and tweaked the line color so it stands out even when the cards aren't in a popup.&lt;/p&gt;

&lt;p&gt;Now the section titles are easy to read and add a nice touch to the whole thing!&lt;/p&gt;

&lt;p&gt;Here is the decluttering-card template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
&lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom:bubble-card&lt;/span&gt;
  &lt;span class="na"&gt;card_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;separator&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[name]]'&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[icon]]'&lt;/span&gt;
  &lt;span class="na"&gt;card_layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;large&lt;/span&gt;
  &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;.bubble-name {&lt;/span&gt;
      &lt;span class="s"&gt;font-weight: 100;&lt;/span&gt;
      &lt;span class="s"&gt;font-size: 20px;&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;
    &lt;span class="s"&gt;.bubble-line {&lt;/span&gt;
      &lt;span class="s"&gt;background-color: var(--background-color-2, var(--secondary-background-color));&lt;/span&gt;
      &lt;span class="s"&gt;opacity: 0.2;&lt;/span&gt;
    &lt;span class="s"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Thermostat
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fthermostat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fthermostat.png" alt="thermostat.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Craving more from my smart thermostat display, I used custom-template-card to dynamically change the card's icon based on heating state.&lt;/p&gt;

&lt;p&gt;Color cues joined the party: blue for off, green for achieved comfort, and orange for heating to reach temperature. I even incorporated the target temperature in this customized switch card.&lt;/p&gt;

&lt;p&gt;Now, it's a one-stop shop for efficient and comfortable climate control!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom:config-template-card&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;MODE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;states['[[entity]]'].state"&lt;/span&gt;
  &lt;span class="na"&gt;entities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[[entity]]"&lt;/span&gt;
  &lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom:bubble-card&lt;/span&gt;
    &lt;span class="na"&gt;card_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;button&lt;/span&gt;
    &lt;span class="na"&gt;button_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;switch&lt;/span&gt;
    &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[name]]'&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${MODE&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;===&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'heat'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'mdi:thermometer':&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'mdi:thermometer-off'}"&lt;/span&gt;
    &lt;span class="na"&gt;show_state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
    &lt;span class="na"&gt;card_layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;large&lt;/span&gt;
    &lt;span class="na"&gt;card_mod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;.bubble-button-card-container {&lt;/span&gt;
          &lt;span class="s"&gt;background-color: {% if is_state(config.entity, 'heat') %}{% if state_attr(config.entity, 'temperature') &amp;gt; state_attr(config.entity, 'current_temperature') %}var(--warning-color){% else %}var(--success-color){% endif %}{% else %}var(--info-color){% endif %} !important;&lt;/span&gt;
        &lt;span class="s"&gt;}&lt;/span&gt;
        &lt;span class="s"&gt;.bubble-icon {&lt;/span&gt;
          &lt;span class="s"&gt;color: {% if is_state(config.entity, 'heat') %}{% if state_attr(config.entity, 'temperature') &amp;gt; state_attr(config.entity, 'current_temperature') %}var(--warning-color){% else %}var(--success-color){% endif %}{% else %}var(--info-color){% endif %} !important;&lt;/span&gt;
          &lt;span class="s"&gt;opacity: 0.6 !important;&lt;/span&gt;
        &lt;span class="s"&gt;}&lt;/span&gt;
        &lt;span class="s"&gt;.bubble-state::after {&lt;/span&gt;
          &lt;span class="s"&gt;content: " - {{ state_attr(config.entity, 'temperature') }} °C";&lt;/span&gt;
          &lt;span class="s"&gt;margin-left: 4px;&lt;/span&gt;
        &lt;span class="s"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Weather
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fweather.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fweather.png" alt="weather.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While Bubble Card forms the foundation, the true magic lies in the customization of my weather display. It leverages the power of sub-buttons to showcase a range of essential information: current conditions, daily highs and lows, wind speed, and the anticipated rain forecast.&lt;/p&gt;

&lt;p&gt;But the key is the intuitive color-coding system I implemented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Temperatures&lt;/strong&gt; utilize a blue-to-red gradient, providing a quick glimpse of the expected highs and lows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wind speed&lt;/strong&gt; follows a similar approach, transitioning from neutral to red as intensity increases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rain&lt;/strong&gt; takes center stage with a prominent blue highlight whenever there's a chance of precipitation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This visual language allows me to grasp the daily forecast at a glance, without getting bogged down in numbers. It's a testament to the power of visual communication and user experience design in action!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
&lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom:bubble-card&lt;/span&gt;
  &lt;span class="na"&gt;card_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;button&lt;/span&gt;
  &lt;span class="na"&gt;button_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;state&lt;/span&gt;
  &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[name]]'&lt;/span&gt;
  &lt;span class="na"&gt;show_state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;scrolling_effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;card_layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;large-2-rows&lt;/span&gt;
  &lt;span class="na"&gt;sub_button&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Min&lt;/span&gt;
      &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mdi:thermometer-low&lt;/span&gt;
      &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
      &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;forecast[0].templow&lt;/span&gt;
      &lt;span class="na"&gt;show_background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;show_attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wind&lt;/span&gt;
      &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mdi:weather-windy&lt;/span&gt;
      &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
      &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wind_speed&lt;/span&gt;
      &lt;span class="na"&gt;show_background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;show_attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;show_icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Max&lt;/span&gt;
      &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mdi:thermometer-high&lt;/span&gt;
      &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
      &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;forecast[0].temperature&lt;/span&gt;
      &lt;span class="na"&gt;show_background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;show_attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rain&lt;/span&gt;
      &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mdi:weather-pouring&lt;/span&gt;
      &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
      &lt;span class="na"&gt;show_background&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;show_attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;attribute&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;forecast[0].precipitation&lt;/span&gt;
      &lt;span class="na"&gt;show_icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;card_mod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-icon-container {&lt;/span&gt;
        &lt;span class="s"&gt;background: transparent;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-name {&lt;/span&gt;
          &lt;span class="s"&gt;border-radius: 20px;&lt;/span&gt;
          &lt;span class="s"&gt;background-color:&lt;/span&gt;
          &lt;span class="s"&gt;{% if state_attr(config.entity, 'temperature') &amp;lt; 10 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--info-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% elif state_attr(config.entity, 'temperature') &amp;gt; 40 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--error-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% elif state_attr(config.entity, 'temperature') &amp;gt; 30 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--warning-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% endif %}&lt;/span&gt;
          &lt;span class="s"&gt;;&lt;/span&gt;
          &lt;span class="s"&gt;margin-left: -8px;&lt;/span&gt;
          &lt;span class="s"&gt;padding: 0 8px;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-name::after {&lt;/span&gt;
          &lt;span class="s"&gt;content: " - {{ state_attr(config.sub_button[0].entity, 'temperature')  }}°C";&lt;/span&gt;
          &lt;span class="s"&gt;margin-left: 4px;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-sub-button-1 {&lt;/span&gt;
          &lt;span class="s"&gt;background-color:&lt;/span&gt;
          &lt;span class="s"&gt;{% if state_attr(config.sub_button[0].entity, 'forecast')[0].templow &amp;lt; 10 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--info-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% elif state_attr(config.sub_button[0].entity, 'forecast')[0].templow &amp;gt; 40 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--error-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% elif state_attr(config.sub_button[0].entity, 'forecast')[0].templow &amp;gt; 30 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--warning-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% endif %}&lt;/span&gt;
          &lt;span class="s"&gt;;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-sub-button-1::before {&lt;/span&gt;
        &lt;span class="s"&gt;content: "°C";&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-sub-button-2 {&lt;/span&gt;
          &lt;span class="s"&gt;background-color:&lt;/span&gt;
          &lt;span class="s"&gt;{% if state_attr(config.sub_button[0].entity, 'wind_speed') &amp;gt; 20 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--warning-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% elif state_attr(config.sub_button[0].entity, 'wind_speed') &amp;gt; 50 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--error-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% endif %}&lt;/span&gt;
          &lt;span class="s"&gt;;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-sub-button-2::before {&lt;/span&gt;
        &lt;span class="s"&gt;content: "km/h";&lt;/span&gt;
        &lt;span class="s"&gt;margin-left: 2px;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-sub-button-3 {&lt;/span&gt;
          &lt;span class="s"&gt;background-color:&lt;/span&gt;
          &lt;span class="s"&gt;{% if state_attr(config.sub_button[2].entity, 'forecast')[0].temperature &amp;lt; 10 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--info-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% elif state_attr(config.sub_button[2].entity, 'forecast')[0].temperature &amp;gt; 40 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--error-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% elif state_attr(config.sub_button[2].entity, 'forecast')[0].temperature &amp;gt; 30 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--warning-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% endif %}&lt;/span&gt;
          &lt;span class="s"&gt;;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-sub-button-3::before {&lt;/span&gt;
        &lt;span class="s"&gt;content: "°C";&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-sub-button-4 {&lt;/span&gt;
          &lt;span class="s"&gt;background-color:&lt;/span&gt;
          &lt;span class="s"&gt;{% if state_attr(config.sub_button[3].entity, 'forecast')[0].precipitation &amp;gt; 0 %}&lt;/span&gt;
            &lt;span class="s"&gt;var(--info-color)&lt;/span&gt;
          &lt;span class="s"&gt;{% endif %}&lt;/span&gt;
          &lt;span class="s"&gt;;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;
      &lt;span class="s"&gt;.bubble-sub-button-4::before {&lt;/span&gt;
        &lt;span class="s"&gt;content:" mm";&lt;/span&gt;
        &lt;span class="s"&gt;margin-left: 2px;&lt;/span&gt;
      &lt;span class="s"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Weather forecast
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fweather-forecast.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2Fbubble-ha%2Fweather-forecast.png" alt="weather-forecast.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While the current weather is important, I also crave a sneak peek at the next week's forecast.&lt;/p&gt;

&lt;p&gt;To achieve this, I leveraged the same color-coded system from my weather card for a visually cohesive experience. Additionally, I cleverly formatted the current day using its attributes, while also extracting the high and low temperatures from the same source.&lt;/p&gt;

&lt;p&gt;This way, I get a clear picture of the upcoming week's weather trends right on my dashboard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom:config-template-card&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;FORECAST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;states['[[entity]]'].attributes.forecast[[[offset]]].condition"&lt;/span&gt;
  &lt;span class="na"&gt;entities&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[[entity]]"&lt;/span&gt;
  &lt;span class="na"&gt;card&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;custom:bubble-card&lt;/span&gt;
    &lt;span class="na"&gt;card_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;button&lt;/span&gt;
    &lt;span class="na"&gt;button_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;state&lt;/span&gt;
    &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[[entity]]'&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Forecast&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${FORECAST&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;===&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'partlycloudy'&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'mdi:weather-partly-cloudy':&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'mdi:weather-'+FORECAST}"&lt;/span&gt;
    &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
    &lt;span class="na"&gt;card_layout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;large&lt;/span&gt;
    &lt;span class="na"&gt;show_state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;card_mod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;.&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;.bubble-name {&lt;/span&gt;
            &lt;span class="s"&gt;color: transparent;&lt;/span&gt;
            &lt;span class="s"&gt;white-space: nowrap;&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
          &lt;span class="s"&gt;.bubble-name:before {&lt;/span&gt;
            &lt;span class="s"&gt;color: var(--primary-text-color);&lt;/span&gt;
            &lt;span class="s"&gt;content: "{{ strptime(state_attr(config.entity, 'forecast')[[[offset]]].datetime, '%Y-%m-%dT%H:%M:%S%z').strftime("%A")  }}";&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
          &lt;span class="s"&gt;.bubble-state {&lt;/span&gt;
            &lt;span class="s"&gt;color: transparent;&lt;/span&gt;
            &lt;span class="s"&gt;white-space: nowrap;&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
          &lt;span class="s"&gt;.bubble-state:before {&lt;/span&gt;
            &lt;span class="s"&gt;color: var(--primary-text-color);&lt;/span&gt;
            &lt;span class="s"&gt;content: "{{ state_attr(config.entity, 'forecast')[[[offset]]].templow }} / {{ state_attr(config.entity, 'forecast')[[[offset]]].temperature }} {{ state_attr(config.entity, 'temperature_unit') }}";&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
          &lt;span class="s"&gt;.bubble-button-card {&lt;/span&gt;
            &lt;span class="s"&gt;background-color:&lt;/span&gt;
            &lt;span class="s"&gt;{% if state_attr(config.entity, 'forecast')[[[offset]]].temperature &amp;lt; 10 %}&lt;/span&gt;
              &lt;span class="s"&gt;var(--info-color)&lt;/span&gt;
            &lt;span class="s"&gt;{% elif state_attr(config.entity, 'forecast')[[[offset]]].temperature &amp;gt; 40 %}&lt;/span&gt;
              &lt;span class="s"&gt;var(--error-color)&lt;/span&gt;
            &lt;span class="s"&gt;{% elif state_attr(config.entity, 'forecast')[[[offset]]].temperature &amp;gt; 30 %}&lt;/span&gt;
              &lt;span class="s"&gt;var(--warning-color)&lt;/span&gt;
            &lt;span class="s"&gt;{% endif %}&lt;/span&gt;
            &lt;span class="s"&gt;!important;&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
          &lt;span class="s"&gt;.bubble-icon {&lt;/span&gt;
            &lt;span class="s"&gt;color: &lt;/span&gt;
            &lt;span class="s"&gt;{% if state_attr(config.entity, 'forecast')[[[offset]]].temperature &amp;lt; 10 %}&lt;/span&gt;
              &lt;span class="s"&gt;var(--info-color)&lt;/span&gt;
            &lt;span class="s"&gt;{% elif state_attr(config.entity, 'forecast')[[[offset]]].temperature &amp;gt; 40 %}&lt;/span&gt;
              &lt;span class="s"&gt;var(--error-color)&lt;/span&gt;
            &lt;span class="s"&gt;{% elif state_attr(config.entity, 'forecast')[[[offset]]].temperature &amp;gt; 30 %}&lt;/span&gt;
              &lt;span class="s"&gt;var(--warning-color)&lt;/span&gt;
            &lt;span class="s"&gt;{% endif %} !important;&lt;/span&gt;
            &lt;span class="s"&gt;opacity: 0.6 !important;&lt;/span&gt;
          &lt;span class="s"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Is that it?
&lt;/h3&gt;

&lt;p&gt;While I've highlighted some key customizations, there are additional tweaks throughout my dashboard that refine the presentation further. These refinements build upon the techniques described here. If you're curious to delve deeper, feel free to reach out – I'm always happy to chat about Home Assistant!&lt;/p&gt;

&lt;p&gt;For a complete picture, here's a quick reference of the custom cards I've utilized:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alarm Status:&lt;/strong&gt; &lt;code&gt;bubble_alarm&lt;/code&gt; (presented earlier)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Air Quality Index:&lt;/strong&gt; &lt;code&gt;bubble_aqi&lt;/code&gt; (presented earlier)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cover Cards:&lt;/strong&gt; &lt;code&gt;bubble_cover&lt;/code&gt; (presented earlier)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Section Separators:&lt;/strong&gt; &lt;code&gt;bubble_separator&lt;/code&gt; (presented earlier)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temperature Display:&lt;/strong&gt; &lt;code&gt;bubble_temperature&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thermostat Control:&lt;/strong&gt; &lt;code&gt;bubble_thermostat&lt;/code&gt; (presented earlier)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trash Collection:&lt;/strong&gt; &lt;code&gt;bubble_trash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vacuum Status:&lt;/strong&gt; &lt;code&gt;bubble_vacuum&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weather Display:&lt;/strong&gt; &lt;code&gt;bubble_weather&lt;/code&gt; (presented earlier)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Météo-France Alerts:&lt;/strong&gt; &lt;code&gt;bubble_weather_alert&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future Forecast:&lt;/strong&gt; &lt;code&gt;bubble_weather_forcecast&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website Status:&lt;/strong&gt; &lt;code&gt;bubble_website&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>homeassistant</category>
      <category>homeautomation</category>
      <category>dashboard</category>
      <category>yaml</category>
    </item>
    <item>
      <title>Mastering Time: Using Fake Timers with Vitest</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Sun, 30 Jun 2024 12:08:59 +0000</pubDate>
      <link>https://dev.to/brunosabot/mastering-time-using-fake-timers-with-vitest-390b</link>
      <guid>https://dev.to/brunosabot/mastering-time-using-fake-timers-with-vitest-390b</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/fr/@aronvisuals?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Aron Visuals&lt;/a&gt; on &lt;a href="https://unsplash.com/fr/photos/un-gros-plan-dun-pissenlit-avec-un-coucher-de-soleil-en-arriere-plan-2NWBmlBTSIE?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the world of testing, controlling time can be a real challenge. Real-world timers, like &lt;code&gt;setTimeout()&lt;/code&gt; and &lt;code&gt;setInterval()&lt;/code&gt;, can be cumbersome when writing unit tests: without the right technique, you will introduce external dependencies on actual time passing that will make your test either slower, wrong or difficult to understand.&lt;/p&gt;

&lt;p&gt;This is where Vitest’s fake timers come in, giving you the power to manipulate time within your tests for a smoother and more efficient testing experience.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;At&lt;/em&gt; &lt;a href="https://playplay.com/"&gt;&lt;em&gt;PlayPlay&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, we create high-quality software while prioritizing efficient development practices. We leverage innovative tools like Vitest’s fake timers to write faster and more reliable tests, ensuring exceptional software from the ground up.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Fake Timers?
&lt;/h3&gt;

&lt;p&gt;Imagine testing a function that debounces another one after a 2-second delay. Using real timers, your test would have to wait for the full 2 seconds to pass, making the whole test scenario longer by these 2 seconds. This is slow and inefficient, and even more when you’re dealing with multiple timers or complex timing interactions. Hopefully, fake timers are here to allow you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Speed up tests:&lt;/strong&gt; Advance the virtual clock by any amount, making tests run significantly faster. This is particularly beneficial for tests that involve waiting for timers to expire or simulating longer time intervals. Vitest prioritizes test speed. Fake timers become even more crucial when dealing with functions that rely on timers. You can avoid waiting for long intervals, keeping your tests lightning fast.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Isolate functionality:&lt;/strong&gt; By removing reliance on external timers, you ensure your tests focus solely on the code you’re testing. This eliminates external factors that could potentially cause flaky tests and makes it easier to pinpoint the source of any issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simulate specific timeouts:&lt;/strong&gt; Test how your code behaves under different time constraints. Fake timers allow you to create scenarios with specific delays or timeouts, helping you ensure your code functions as expected in various situations. You can check something’s state just before the timer is executed and right after.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting Started with Fake Timers
&lt;/h3&gt;

&lt;p&gt;Vitest provides the &lt;code&gt;vi.useFakeTimers()&lt;/code&gt; function to enable fake timers: this mocks out the behavior of &lt;code&gt;setTimeout()&lt;/code&gt;, &lt;code&gt;setInterval()&lt;/code&gt;, &lt;code&gt;clearTimeout()&lt;/code&gt;, and &lt;code&gt;clearInterval()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can call this method globally, before each test or on-demand. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;beforeEach&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;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;beforeEach&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;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&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;If you chose to do it on demand, you will need to restore the real behavior to ensure subsequent tests don’t inherit the fake timer behavior. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterEach&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;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;afterEach&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;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRealTimers&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;
  
  
  Basic Usage
&lt;/h3&gt;

&lt;p&gt;Let’s start with a simple example. Imagine you have a function that uses &lt;code&gt;setTimeout()&lt;/code&gt; to execute a callback after a delay:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getDelayedGreeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&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="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test this function with fake timers, you can write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&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;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterEach&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;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;afterEach&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;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRealTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Given the getDelayedGreeting function&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;When we wait 1s&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Then it calls the callback with the right message&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;// Arrange&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&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;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;getDelayedGreeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Act&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;advanceTimersByTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Assert&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World!&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;In this test, &lt;code&gt;vi.advanceTimersByTime(1000)&lt;/code&gt; fast-forwards the timer by 1000 milliseconds, causing the &lt;code&gt;setTimeout()&lt;/code&gt; to fire immediately. The &lt;code&gt;expect()&lt;/code&gt; assertion then checks if the callback was called with the correct argument.&lt;/p&gt;

&lt;p&gt;The true power of fake timers lies in their ability to ensure that time advances exactly as expected in your tests.. We can write the non-passing test as easy as the previous one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&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;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterEach&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;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;afterEach&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;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRealTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Given the getDelayedGreeting function&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;When we wait 0.999s&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="nf"&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;Then the callback hasn't been called yet&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;// Arrange&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&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;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;getDelayedGreeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Act&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;advanceTimersByTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Assert&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&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;h3&gt;
  
  
  Advanced Usage
&lt;/h3&gt;

&lt;p&gt;When writing tests, we will put a great importance to having the right situation at the right time. If the previous example is working, occasional delay could occurs and make the test flaky. So to ensure nothing more will happen with time passing, we would ideally cancel every running timer.&lt;/p&gt;

&lt;p&gt;That’s where &lt;code&gt;vi.clearAllTimers()&lt;/code&gt; can help us: by cancelling everything that is running, we make sure that nothing will prevent our test to work as expected. Here is the previous example improved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&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;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterEach&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;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;afterEach&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;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRealTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Given the getDelayedGreeting function&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;When we wait 0.999s&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="nf"&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;Then the callback hasn't been called yet&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;// Arrange&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&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;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;getDelayedGreeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Act&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;advanceTimersByTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearAllTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Assert&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that any timers scheduled after the specified time advancement with &lt;code&gt;vi.advanceTimersByTime()&lt;/code&gt; won't interfere with the assertion, making our tests safe.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;vi.clearAllTimers()&lt;/code&gt; after &lt;code&gt;vi.advanceTimersByTime()&lt;/code&gt;ensures that no timers scheduled after the specified time advancement will interfere with the assertion. This makes your test more robust and less susceptible to unexpected timeouts caused by lingering timers.&lt;/p&gt;

&lt;p&gt;But sometimes, what we want is more than just advancing time: we want to make the timer to be executed no matter how long it lasts.&lt;/p&gt;

&lt;p&gt;Let’s imagine a game that tries to improves your reflexes: the callback will wait a random timeout before calling the callback where a mystery number will be display for you to enter. The method would look like:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getMysteryNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Get a delay between 1 and 6 seconds&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&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="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be sure that the method is working, we can use &lt;code&gt;vi.runPendingTimers()&lt;/code&gt; to execute the timeout no matter the delay:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&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;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterEach&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;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;afterEach&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;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRealTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Given the getMysteryNumber function&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;When we wait the delay to be completed&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Then the callback is called with the mystery number&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;// Arrange&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;random&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mockImplementationOnce&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&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;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;getMysteryNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Act&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runOnlyPendingTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Assert&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenNthCalledWith&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="mi"&gt;5&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;Here, we use &lt;code&gt;vi.spyOn()&lt;/code&gt; to stub the &lt;code&gt;Math.random()&lt;/code&gt; function and mock its behavior to return a specific value (0.5) for the delay. This allows us to control the randomness and ensure a consistent delay of 5 seconds in the test.&lt;/p&gt;

&lt;p&gt;Now, no matter how long the delay is supposed to be, the test will be executed quickly.&lt;/p&gt;

&lt;p&gt;But what if you have an interval timer that stop after a specific amount of time? For example, you have a method that counts up to five seconds:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;intervalId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&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="nx"&gt;count&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;count&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Done!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;intervalId&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="mi"&gt;1000&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 interval timers that complete after a specific duration, you can use &lt;code&gt;vi.advanceTimersByTime()&lt;/code&gt; to advance the virtual clock by the expected interval duration. This approach often provides more control over the test execution. However, &lt;code&gt;vi.runAllTimers()&lt;/code&gt; is a viable alternative when you need to ensure all timers, including intervals, are run to completion. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&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;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;afterEach&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;vitest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;afterEach&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;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRealTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Given the startCounter function&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;When we wait 0.999s&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="nf"&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;Then the callback hasn't been called yet&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;// Arrange&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useFakeTimers&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;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nf"&gt;startCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Act&lt;/span&gt;
            &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runAllTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Assert&lt;/span&gt;
            &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenNthCalledWith&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Done!&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;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Fake timers in Vitest provide a robust way to test time-dependent code : they allow you to write faster, more reliable, and more focused unit tests.&lt;/p&gt;

&lt;p&gt;By simulating the passage of time, you can write tests that are both fast and deterministic. Whether you are dealing with simple timeouts or complex interval logic, Vitest’s fake timers have you covered.&lt;/p&gt;

&lt;p&gt;With this guide, you should now have a solid understanding of how to use fake timers in your Jest tests. Experiment with these techniques in your own projects to achieve more effective and maintainable tests. Happy testing! For further details and advanced usage, refer to the Vitest &lt;a href="https://vitest.dev/advanced/api.html"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>vitest</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Improve Performances With Dynamic “Content-Visibility”</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Mon, 26 Jun 2023 19:05:42 +0000</pubDate>
      <link>https://dev.to/brunosabot/improve-performances-with-dynamic-content-visibility-8bb</link>
      <guid>https://dev.to/brunosabot/improve-performances-with-dynamic-content-visibility-8bb</guid>
      <description>&lt;h3&gt;
  
  
  Initial Considerations
&lt;/h3&gt;

&lt;p&gt;This article presents a few technologies that are only available on some browsers. This should not be considered as the solution you can use to solve all your performance issues but as the solution to improve some of your users’ experience.&lt;/p&gt;

&lt;p&gt;Also, you should consider using it more as an experiment than an ultimate performance saver that can help you understand how a browser's rendering works and what can be controlled.&lt;/p&gt;

&lt;p&gt;Finally, the code could also be improved by doing several enhancements — performance or features — to make it production ready. I use it on some websites, but it hasn’t been tested at scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Content-Visibility CSS Property
&lt;/h3&gt;

&lt;p&gt;When we have a big page with a huge amount of data, the browser will struggle with one of its steps to display the page because it asks for a lot of work.&lt;/p&gt;

&lt;p&gt;The browser will go through the following step:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Loading&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scripting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rendering&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Painting&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bigger the page is, the slower these operations will be. But in this game, the rendering step is an Achilles’ heel for everyone looking for speed. The browser makes a lot of calculations here to prepare the painting, and the more the page is complex, the more time it takes.&lt;/p&gt;

&lt;p&gt;Unlike a house where you can do the painting once the construction is complete — and maybe once in a while for a deserved refreshing — a browser has many reasons to make a lot of renderings over time. JavaScript DOM modifications, scrolls, dynamic carousels, and so on.&lt;/p&gt;

&lt;p&gt;To help developers take more control over this, Chromium has introduced a new CSS property call &lt;code&gt;content-visibility&lt;/code&gt; that aims to solve this performance by skipping this rendering part when it is not necessary, mainly because the concerned block is out of the screen, which, in our single page applications, can happen a lot: have you ever seen an e-commerce website without a vertical scroll?&lt;/p&gt;

&lt;p&gt;This newly introduced property takes three possible values, which are &lt;code&gt;visible&lt;/code&gt;, &lt;code&gt;hidden&lt;/code&gt; and &lt;code&gt;auto&lt;/code&gt;. As you can imagine, the first one, &lt;code&gt;visible&lt;/code&gt; exist to tell the browser that a block should always be rendered, no matter if it is on-screen or not, while the &lt;code&gt;hidden&lt;/code&gt; value always hides the element from rendering. Since we want to improve our performances, the last one is the one that is the most interesting for us: it will make the browser look at the supposed position of a given block and decide by itself if the block should be rendered or not.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Does It Work?
&lt;/h3&gt;

&lt;p&gt;Setting this CSS property to &lt;code&gt;auto&lt;/code&gt; will prevent the browser from executing the rendering part of the displaying, saving a lot of execution time (rendering + painting) on all the hidden elements of the page. Then, as soon as the elements appear on the screen, the rendering will be made, and you can see your content properly.&lt;/p&gt;

&lt;p&gt;However, if it hides the rendering, you should not be concerned about SEO issues or having accessibility consequences: all the document model -and therefore the accessibility tree- is available: web crawlers will not suffer from any issues, and the screen readers will work as expected. You should also remember that this CSS property is experimental, so the old-fashioned system will ignore it until it is correctly implemented.&lt;/p&gt;

&lt;p&gt;Finally, once the element quits the screen, it is removed from your page rendering. It will then save another list of potential heavy calculations, such as if you have &lt;code&gt;@keyframes&lt;/code&gt; animations, for example.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting the Block Size
&lt;/h3&gt;

&lt;p&gt;As the content is not always rendered on your page, one thing might look a bit strange: your window height will not be the full-rendered one, so scrollbars won’t be able to do its proper height. With content removed from rendering as it leaves the page, this issue is not only a problem at the bottom but also at the top: the upper part of the screen is removed from the page height as you scroll it out of the screen.&lt;/p&gt;

&lt;p&gt;So, to make sure the space is reserved, you can use another CSS property there reserve this very space when using the &lt;code&gt;content-visibility&lt;/code&gt; CSS property: it is &lt;code&gt;contain-intrinsic-size&lt;/code&gt; that you can use to set the width and height of the elements. &lt;code&gt;content-intrinsic-size: 400px 300px&lt;/code&gt; will tell the browser that the associated block is 400px width and 300px height.&lt;/p&gt;

&lt;h3&gt;
  
  
  But…
&lt;/h3&gt;

&lt;p&gt;It is very easy to use if you want to display a card as you have fixed dimensions. Still, sometimes, you might not know the size of the element you are not rendering, such as in a blog post: paragraphs have various sizes, and code blocks can also have differences in dimensions. And blog posts will likely be long and complicated. Hello, syntax highlighting on code blocks.&lt;/p&gt;

&lt;p&gt;It is a shame that our performances won’t have the amazing benefit of this powerful feature.&lt;/p&gt;

&lt;p&gt;Hopefully, a few other amazing APIs in the browser can come to the rescue!&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;IntersectionObserver&lt;/code&gt; JavaScript object
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;IntersectionObserver&lt;/code&gt; is an API that detects when a block gets visible or leaves the screen viewport, which is pretty much the same, but using &lt;code&gt;content-visibility&lt;/code&gt; this time, it is JavaScript, so it gets more interactive and customizable.&lt;/p&gt;

&lt;p&gt;It has a lot of awesome usages, such as loading the next or previous pages in an infinite loading page as soon as the footer gets into the viewport, pausing a video if you scroll it out of the screen, or lazy loading images -even tho you should use &lt;code&gt;loading="lazy"&lt;/code&gt; instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Does It Work?
&lt;/h3&gt;

&lt;p&gt;To use an &lt;code&gt;IntersectionObserver&lt;/code&gt; you will need three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A callback function will be executed as the element enters or leaves the screen's visible part. This is where your magic will happen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An observer object that will handle the observation configuration, such as the sensibility of the detection. It also means that you can use the same configuration object for every block of your code you want to observe, reducing the impact on the memory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;An attachment to a DOM object that we will observe. In our scenario, it will be the block with a dynamic height on which we can’t use the &lt;code&gt;content-visibility&lt;/code&gt; property.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enough with the words. Here is a very simple code example of how to get started:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Deep Dive Into the Callback Method
&lt;/h3&gt;

&lt;p&gt;As you saw in the previous gist, the callback method takes two parameters.&lt;/p&gt;

&lt;p&gt;The first one is the list of intersections that have just occurred. It is an object with many parameters that might be used in different use cases, but we can focus on the two most important for today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;isIntersecting&lt;/code&gt; which is a boolean set to &lt;code&gt;true&lt;/code&gt; appears according to the configuration &lt;code&gt;false&lt;/code&gt;, otherwise. That’s the only information useful in our use case, as we want to know if the element has reached the viewport and thus has been rendered and now has a real height.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;target&lt;/code&gt; which is the element that is currently intersecting. It will be the same as the DOM object we listened to, but since a single &lt;code&gt;IntersectionObserver&lt;/code&gt; can be used many times, this property is more reliable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second parameter is the observer instance. It allows us to start listening to another element if we make an infinite loading system. Still, in our case, we can stop the observation as soon as we collect the height.&lt;/p&gt;

&lt;p&gt;Now, let’s see how we can link it to the &lt;code&gt;content-visibility&lt;/code&gt;feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle Blocks With Dynamic Heights
&lt;/h3&gt;

&lt;p&gt;Linking &lt;code&gt;IntersectionObserver&lt;/code&gt; and &lt;code&gt;content-visibilty&lt;/code&gt; is now very simple: we have every tool we need, and we have to add the smart behavior in the callback method to make an overall improvement on our webpage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;First, we create an &lt;code&gt;IntersectionObserver&lt;/code&gt; to detect visibility changes in a given block. When the detection is done, we will get into the callback method.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside the callback method, we can collect the given element's actual size since the browser has rendered it. It is as simple as &lt;code&gt;const { height } = entry.target.getBoundingClientRect();&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We can now set the CSS properties on the object. It is a one-liner: &lt;code&gt;entry.target.style.containIntrasicSize = `${height}px`&lt;/code&gt; . There is no need to set the &lt;code&gt;content-visibility&lt;/code&gt;: it should have already been assigned in your CSS to make this work. If you haven’t already, you will lose the benefit of delaying the rendering part.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, since the height is now assigned, we don’t need the observation anymore, so we can stop the observation on this block&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is a code example merging these two features:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Handling Responsiveness
&lt;/h3&gt;

&lt;p&gt;There is one final touch we can bring to our code to make it more production ready: if the window gets resized, some blocks might have a changed height, and thus, our assigned value is not relevant anymore. And that’s the beauty of responsive designs.&lt;/p&gt;

&lt;p&gt;To ensure the height is always the right one, we can just observe the &lt;code&gt;resize&lt;/code&gt; &lt;code&gt;window&lt;/code&gt; event, remove the assigned height and bring the observer back to business. As soon as the element crosses the visual part of the page, its height will be recalculated.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;We are now ready to make our pages a lot faster! These two light APIs allow the developer to be more in control of what the page needs to display at any time. We can benefit from the power of &lt;code&gt;content-visibility&lt;/code&gt; event on blocks with a dynamic height!&lt;/p&gt;

&lt;p&gt;If you are working with frameworks like me, you can find a React hook and a Vue composable you can reuse in your applications at the end of this blog post.&lt;/p&gt;

&lt;p&gt;Feel free to give me feedback on your Core Web Vitals improvements with this snippet!&lt;/p&gt;

&lt;h3&gt;
  
  
  React Hook
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Vue Composable
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;





&lt;blockquote&gt;
&lt;p&gt;Want to Connect?&lt;/p&gt;

&lt;p&gt;If you want to learn more about JavaScript and React, feel free to follow me on &lt;a href="https://twitter.com/brunosabot"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>css</category>
      <category>corewebvitals</category>
      <category>performance</category>
    </item>
    <item>
      <title>JavaScript Spread and Rest Operators: When To, Why To, and How to Use Them</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Fri, 01 Apr 2022 07:05:42 +0000</pubDate>
      <link>https://dev.to/brunosabot/javascript-spread-and-rest-operators-when-to-why-to-and-how-to-use-them-58lo</link>
      <guid>https://dev.to/brunosabot/javascript-spread-and-rest-operators-when-to-why-to-and-how-to-use-them-58lo</guid>
      <description>&lt;p&gt;Ecmascript has introduced the rest and spread operators a few years ago. It is a powerful tool that helps a lot our code to be more concise and cleaner.&lt;/p&gt;

&lt;p&gt;However, it is not that easy to understand how it works at first and what is the main reason they do not have the same name.&lt;/p&gt;

&lt;p&gt;In this blog post, I will explain the naming, how it is working, and in which situation it is a time saver.&lt;/p&gt;

&lt;h2&gt;
  
  
  Assignment shortcuts
&lt;/h2&gt;

&lt;p&gt;Before we talk about the rest operator, you need to understand a new variable assignment method to shorten the code declaration.&lt;/p&gt;

&lt;p&gt;It consists of using an array or an object on the declaration part of an assignment.&lt;/p&gt;

&lt;p&gt;The code looks like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you can see, we are creating an array naming the indexes directly in the assignment part. This code is actually a shortcut and the same of the following code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;If this code is pretty simple, it might be a bit longer and therefore complex- when having a dozen of items in the array.&lt;/p&gt;

&lt;p&gt;This is working the same with objects. Here is the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The only difference with the array is that the naming is very important since we are extracting the object from its keys.&lt;/p&gt;

&lt;p&gt;If for any reason, you need to change the name (it could be a duplicate key), you can use the &lt;code&gt;:&lt;/code&gt; operator to change the name. Here is an example:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you can see, we have a duplicate red key that we had assigned to a &lt;code&gt;frenchRed&lt;/code&gt; variable and a &lt;code&gt;belgianRed&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;If you acknowledge this, you can now start the next part about the rest operator, that uses this variable assignment method.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest operator
&lt;/h2&gt;

&lt;p&gt;The name has been chosen wisely, the rest operator is a method to select the rest of the data that hasn't been already assigned.&lt;/p&gt;

&lt;p&gt;This operator uses three dots &lt;code&gt;...&lt;/code&gt; as a symbol to tell the JavaScript parser that we are using the rest feature.&lt;/p&gt;

&lt;p&gt;But one point is really important to understand: the rest operator is only used on the assignment part of a JavaScript code. We saw the new assignment method in the previous chapter, but the reason it is important to link rest and assignment will be discussed later.&lt;/p&gt;

&lt;p&gt;The rest operator works on the array since the beginning of the feature introduction, and a bit later on objects. If you managed to make it work on other JavaScript types, you are using coercion which I recommend you not to use. But this might be the content of another blog post.&lt;/p&gt;

&lt;p&gt;Now that the basics are ok, let's see some code on how it looks:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you can see in the previous code, the assignment is the same than the first chapter, but we add a single variable name with three dots &lt;code&gt;...&lt;/code&gt; instead of the whole elements left.&lt;/p&gt;

&lt;p&gt;This will tell the JavaScript parser to create an array with anything else in the first assignment, and an object with anything else in the second one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we need the rest operator?
&lt;/h2&gt;

&lt;p&gt;We mainly need this operator to extract a part of the array for a specific behavior and use the rest for something else.&lt;/p&gt;

&lt;p&gt;Imagine you have an array with every soccer player of a team and you want to separate the goalkeeper from the field players, so you can give them the instruction to go full attack. You can do it with the following code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Of course, you can select two items if you want to keep a defender in the field:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;You can add as many variable declarations in the assignment, but you can not skip some variables in the rest argument. For this, you will need the spread method we'll see later.&lt;/p&gt;

&lt;p&gt;For objects, the rest operator is very useful when you want to remove an element from the object you want to forward.&lt;/p&gt;

&lt;p&gt;Imagine you are developing a Node.js service to manage users, and you want to send to the client everything from the object except the password and the password's salt... In this case, the rest operator will help you.&lt;/p&gt;

&lt;p&gt;Here is the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;With this, you can simply return the right data for the client's request&lt;/p&gt;

&lt;h2&gt;
  
  
  The spread operator
&lt;/h2&gt;

&lt;p&gt;Like the rest operator, the spread operator also uses the three dots &lt;code&gt;...&lt;/code&gt; as a syntax.&lt;/p&gt;

&lt;p&gt;The way to make the difference is to understand that the spread operator will be on the operation part of the variable assignment.&lt;/p&gt;

&lt;p&gt;The goal is pretty the same as the rest operator, but instead of recovering the rest of a variable, we are adding the rest of another variable to a declaration.&lt;/p&gt;

&lt;p&gt;Code speaks better, here is an example of how it works:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;On the previous example, we have combined two objects with the rest operator. If it's still not clear, here is an example that could help.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The syntax is not valid JavaScript, but it illustrates what is done under the hood.&lt;/p&gt;

&lt;p&gt;As seen in that example, using the spread is kind of removing the brackets from our variable. Since it is something invalid in JavaScript, we need to put that in another array, making an array duplication in the meantime.&lt;/p&gt;

&lt;p&gt;In arrays, the position of the rest operator is very important as it places the elements in the specified position.&lt;/p&gt;

&lt;p&gt;Here is an illustration of that:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In the previous sample, I also have another item in my array which is non-spread operator data. It is a perfectly valid thing to do if you want to complete your object with a unitary value. It could be having an array with the last execution result, complete with the previous results.&lt;/p&gt;

&lt;p&gt;For objects, the spread operator works the same. However, as objects are key value items, the order does not really matter but you need to make sure your keys are not duplicated or they will be erased.&lt;/p&gt;

&lt;p&gt;Here is an exemple:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you can see in the previous example, the position of the spread operator is very important in case of duplicated keys.&lt;/p&gt;

&lt;p&gt;As it's the case for arrays, the spread operator removes the braces from the object, but since it's invalid syntax, we are putting it back in another object duplicating it at the same time.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  When do we need the spread operator
&lt;/h2&gt;

&lt;p&gt;As we have seen in one of the previous examples, the spread operator is mainly used to merge arrays from different sources or having defaults key/values when using objects.&lt;/p&gt;

&lt;p&gt;You might find plenty of other usages while developing your JavaScript application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Combining rest and spread and more
&lt;/h2&gt;

&lt;p&gt;Since it rest and spread are not on the same part of the assignment, you can combine both their usage in the same line of code. You should however take care of not using this to make your code harder to read: it will be counterproductive.&lt;/p&gt;

&lt;p&gt;You can also apply the spread operator on a function return, as long as the value returned matches an array or an object depending on your usage.&lt;/p&gt;

&lt;p&gt;Here is an example:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


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

&lt;p&gt;Rest and spread operator are very powerful features you can bring to your web application starting today.&lt;/p&gt;

&lt;p&gt;If it is a bit hard to understand at first, it will make your code cleaner and more robust.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Want to Connect?&lt;/p&gt;

&lt;p&gt;If you want to learn more about JavaScript and React, feel free to follow me on &lt;a href="https://twitter.com/brunosabot"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>softwareengineering</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Loading Gists in a NextJS Application</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Tue, 01 Mar 2022 17:05:42 +0000</pubDate>
      <link>https://dev.to/brunosabot/loading-gists-in-a-nextjs-application-2l99</link>
      <guid>https://dev.to/brunosabot/loading-gists-in-a-nextjs-application-2l99</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;All my blog posts are available on my &lt;a href="https://brunosabot.dev"&gt;personal website&lt;/a&gt;. Under the hood, they are saved as markdown files.&lt;/p&gt;

&lt;p&gt;It allows me to easily write the posts, but when I’m cross-posting on other platforms, I don’t have the luxury to get the syntax highlighting and the right colors for the code snippets.&lt;/p&gt;

&lt;p&gt;To make it prettier on every platform, I’m using Github Gists. It renders well everywhere, but I don’t want to have Github’s code blocs styles on my blog but I prefer something that match my website design. To do so, I have to adapt the markdown parser I use to load and render the code from gists in the way I prefer.&lt;/p&gt;

&lt;p&gt;In this post, we will see how to create a blog post using markdown files, how to create and integrate a remark plugin, highlight the recovered code it with &lt;code&gt;react-syntax-highlighter&lt;/code&gt; and keep the performances as good as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Situation: Loading Markdown in a NextJS Page
&lt;/h2&gt;

&lt;p&gt;Before getting into the code for loading gists, we first need to make the blog to load markdown files. To do so, it needs two different items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A markdown file containing the blog post&lt;/li&gt;
&lt;li&gt;A NextJS page containing the markdown loading, parsing and rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s start with the markdown file&lt;/p&gt;

&lt;h3&gt;
  
  
  What Does The Markdown File Looks Like
&lt;/h3&gt;

&lt;p&gt;The markdown file is separated in two different parts: the metadata and the content itself.&lt;/p&gt;

&lt;p&gt;The metadata are here to give informations on the author, the date, the language used (I have posts both in french and english), the URL path, tags, etc.&lt;/p&gt;

&lt;p&gt;These informations are isolated from the content by three dashes --- at the begin and three and the end.&lt;/p&gt;

&lt;p&gt;Here is a light sample of what a blog post can look like:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The file is very simple. If you are not familiar with markdown, you can &lt;a href="https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax"&gt;read this doc from Github&lt;/a&gt; to get started: you will learn how to create lists, links, add image and more.&lt;/p&gt;

&lt;p&gt;In the reality of my bog, I have more metadata fields to handle canonical links, image, tags and more. I removed them from that post as it is unnecessary noise for its topic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading The Markdown File
&lt;/h3&gt;

&lt;p&gt;The first part to render a markdown file is to load it. We are using basic NodeJS code to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lookup all files in a folder&lt;/li&gt;
&lt;li&gt;Filter the md/mdx files only&lt;/li&gt;
&lt;li&gt;Load the file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It might be overkill, but on my blog, I need to get every posts to find the most similar posts of my current article, based on a calculation on every common tags they have. I will have to load every post to find out anyway.&lt;/p&gt;

&lt;p&gt;Here is the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;If you are creating a simpler blog, you might prefer to directly load the right file instead of all of them. To do so, you just need to use the &lt;code&gt;fs.readFileSync&lt;/code&gt; function to get your file.&lt;/p&gt;

&lt;p&gt;Now the posts are loaded, we need to search inside the file which one is the right one for our current page.&lt;/p&gt;

&lt;p&gt;To do so, I’m looking for the post with same path as requested, but I first need to parse my markdown for metadata. I’m using a package called &lt;code&gt;grey-matter&lt;/code&gt; to get them.&lt;/p&gt;

&lt;p&gt;Here is the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;loadGreyMatterPost&lt;/code&gt; is a function returning a file representing our markdown at the grey-matter format. We still have to transform it into an HTML code to use it. As explained earlier, I am also returning all the posts to make matches based on the tags. It will however not explained in this post.&lt;/p&gt;

&lt;p&gt;Since I’m using NextJS, I can now use the package &lt;code&gt;next-mdx-remote&lt;/code&gt; which can make the transformation from the grey-matter format to a MDX one.&lt;/p&gt;

&lt;p&gt;Here is the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Adding The Loaded Data Inside a NextJS Route
&lt;/h3&gt;

&lt;p&gt;To avoid enormous and slow payload, the loading part is added inside the NextJS route’s &lt;code&gt;getStaticProps&lt;/code&gt; method. It will only parse the files at the compile time, which is OK with markdown, and serve static pages the faster possible.&lt;/p&gt;

&lt;p&gt;Here is the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Display the blog post in the page
&lt;/h3&gt;

&lt;p&gt;Now we have all the required informations, we can include them in the NextJS page.&lt;/p&gt;

&lt;p&gt;It will need two external components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MDXProvider&lt;/code&gt; which is a component that can handle defaults informations for every children markdown document&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MDXRemote&lt;/code&gt; which transform the MDX content to HTML React components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And it is done! We are now able to write markdown blog post and get them rendered as HTML in our NextJS application!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Remark plugin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The AST transformation
&lt;/h3&gt;

&lt;p&gt;A remark plugin is a function that return another function. That last one gets an AST -Abstract Syntax Tree- representing the markdown document.&lt;/p&gt;

&lt;p&gt;The AST can get numerous fields, but here is a simple typescript interface of how it is structured:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;When using markdown, the AST for code does not have children or attributes, while the paragraph AST does not have any value. To understand how it is built, imagine the following markdown:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The associated AST will be:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;With this in mind, we can think about how we are going to include our gist.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetching a Gist From Github
&lt;/h3&gt;

&lt;p&gt;I chose to include the gist in an inline block code, with a prefix &lt;code&gt;gist:&lt;/code&gt; and then the username and ID. It will look like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;`gist:brunosabot/00000000000000000000000000000000`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When rendering this content, the generated AST will be composed of a &lt;code&gt;paragraph&lt;/code&gt; node with an &lt;code&gt;inlineCode&lt;/code&gt; child. What we need to do is to replace the paragraph the loaded code from Github.&lt;/p&gt;

&lt;p&gt;As we need code from Github, we will also need to fetch the gist. A gist can be made of one to several files: we will first need to iterate on the file list then load each one of them.&lt;/p&gt;

&lt;p&gt;Here is the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The first fetch query gets a JSON containing metadata for the gist, especially the file list in the gist.&lt;/p&gt;

&lt;p&gt;When the list is recovered, we iterate them to load as text the code associated to every file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visiting And Updating The AST
&lt;/h3&gt;

&lt;p&gt;The gist plugin should alter the content of the original AST. We are going to visit the document nodes and update it as we want.&lt;/p&gt;

&lt;p&gt;To visit the different nodes, we use the &lt;code&gt;async-unist-util-visit&lt;/code&gt; package. it will apply a method on every AST child that match the requested type.&lt;/p&gt;

&lt;p&gt;As explain before, we are going to look for &lt;code&gt;inlineCode&lt;/code&gt;, starting with &lt;code&gt;gist:&lt;/code&gt;, inside a &lt;code&gt;paragraph&lt;/code&gt; node, then parse it and load the gist.&lt;/p&gt;

&lt;p&gt;But let’s focus on the visiting code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;One important thing to notice is that the AST must be mutated and not returned. Since &lt;code&gt;visit&lt;/code&gt; is a promise but does not wait for the callback to resolve, we need to use a hack consisting on adding the work in a promise list and wait them to resolve before the plugin method to end.&lt;/p&gt;

&lt;p&gt;In the code snippet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;node.children.some&lt;/code&gt; method is looking for a gist snippet in the code&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Object.assign&lt;/code&gt; method is the way to update the content of the object without loosing the reference&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;delete&lt;/code&gt; is not mandatory, but I like to keep a clear object with only the required fields.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the plugin, we are using a &lt;code&gt;loadAndTransformGist&lt;/code&gt; method that is called when we detect a gist snippet in the code.&lt;/p&gt;

&lt;p&gt;As it written in the function name, this method will load the gist and make the proper transformation to be included in the AST.&lt;/p&gt;

&lt;p&gt;Here its code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In this snippet, the first part is to check it the snippet is valid, which is basically checking if the AST value is empty or not.&lt;/p&gt;

&lt;p&gt;I could make few more checks, including one to validate that the gist value is correct. I choose not to since I’m often checking the rendering of my post: I can see very quick when the ID is wrong since the app will either crash or don’t show anything.&lt;/p&gt;

&lt;p&gt;I’m then extracting the gist ID, load it with the previously created &lt;code&gt;loadGist&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Then, I have two possibilities:&lt;/p&gt;

&lt;p&gt;First, there is only one file in the gist. I will just replace the current paragraph with a bock sample. The &lt;code&gt;getGistAST&lt;/code&gt; will give me the code AST that I will return for the calling method.&lt;/p&gt;

&lt;p&gt;Second, there is multiple files in the gist. I will iterate on them and add them as children of the paragraph node.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;getGistAST&lt;/code&gt; is basically a mapping method with the following code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Translated to english, this AST node is a &lt;code&gt;&amp;lt;Gist&amp;gt;&lt;/code&gt; component, with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A file attribute representing the file name&lt;/li&gt;
&lt;li&gt;A code attribute which is the actual code&lt;/li&gt;
&lt;li&gt;A lang attribute containing the file language, calculated from a mapping method&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here is the mapping method:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And everything is done, we just need to update our &lt;code&gt;transformGreyMatterToMDX&lt;/code&gt; method, adding the plugin:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Using a Custom Rendering Component
&lt;/h2&gt;

&lt;p&gt;Now the markdown is able to read gist snippets, there is a last step to make the actual code rendering: we need to create a &lt;code&gt;Gist&lt;/code&gt; component to make the proper display.&lt;/p&gt;

&lt;p&gt;To do so, I will use the &lt;a href="https://www.npmjs.com/package/react-syntax-highlighter"&gt;React Syntax Highlighter&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;Here what the component looks like:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;We first have a single div, if the file name is available to display it.&lt;/p&gt;

&lt;p&gt;Then, we use &lt;code&gt;Prism&lt;/code&gt; from &lt;code&gt;react-syntax-highlighter&lt;/code&gt; with the following attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;language&lt;/code&gt;: the programming language the snippet is on. If not available, we set &lt;code&gt;text&lt;/code&gt; which a a default and non highlighted language&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;showLineNumbers&lt;/code&gt;: this display line numbers on the side. If text, it is better not to have them&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;wrapLines&lt;/code&gt; and wrapLongLines: activate, when the code is &lt;code&gt;text&lt;/code&gt; wrapping the lines. I found it not easy to read on highlighted code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To be recognized, this Gist component needs to be included in the components given to the &lt;code&gt;MDXProvider&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In bonus, you can see I’m rewriting the img component to add the &lt;code&gt;loading="lazy"&lt;/code&gt; attribute. I will help my blog post to have better performances.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep The Performances As Good As Possible
&lt;/h2&gt;

&lt;p&gt;One problem with React Syntax Highlighter is that it will load a lot of language you might never need.&lt;/p&gt;

&lt;p&gt;Hopefully, there is a way to use a lighter version of the library, but it require us to load the languages manually.&lt;/p&gt;

&lt;p&gt;Here is the &lt;code&gt;&amp;lt;Gist&amp;gt;&lt;/code&gt; component code with the loading system:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


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

&lt;p&gt;Creating a Gist plugin for a NextJS blog requires us to know about and manipulate the AST format which might not be trivia.&lt;/p&gt;

&lt;p&gt;I hope this post can help you understand how it works and how you can also add your gists files right into your blog.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to learn more about JavaScript and React, feel free to follow me on &lt;a href="https://twitter.com/brunosabot"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>programming</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Fetching Data With React</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Tue, 01 Feb 2022 19:01:08 +0000</pubDate>
      <link>https://dev.to/brunosabot/fetching-data-with-react-48k5</link>
      <guid>https://dev.to/brunosabot/fetching-data-with-react-48k5</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@sanderweeteling"&gt;Sander Weeteling&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;When we create a React application, there are a lot of chances you will have to fetch data from a remote server.&lt;/p&gt;

&lt;p&gt;Depending on your project, you will probably have to make simple calls or use advanced techniques to get your data cached or up to date.&lt;/p&gt;

&lt;p&gt;In this blog post, we will discuss custom-made data fetching but also have a quick preview of &lt;a href="https://react-query.tanstack.com/"&gt;React Query&lt;/a&gt; and &lt;a href="https://swr.vercel.app/"&gt;SWR&lt;/a&gt;. Let's take a tour of these common techniques so you can pick the right ones for your use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic Data Fetch
&lt;/h3&gt;

&lt;p&gt;To explain how to make a custom data fetching, we will pass through a few steps to understand how to make the code robust enough, based on what it can be to think about the perfect loading system.&lt;/p&gt;

&lt;p&gt;If you are not confident enough to manage it on your own, I recommend you go directly to the last sections on SWR and React Query.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the JavaScript Fetch Method
&lt;/h3&gt;

&lt;p&gt;To get a basic data fetching working, we only need a place that will contain the recovered data and a method to make the actual fetching.&lt;/p&gt;

&lt;p&gt;Speaking of the code, it consists of the &lt;code&gt;useEffect&lt;/code&gt; hook to make the data fetching and a &lt;code&gt;useState&lt;/code&gt; hook that will store the data as soon as the request end.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As you can see, you can get your data from your remote server in just a few lines of code. Gathering data is as simple as that with React.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separating Concerns
&lt;/h3&gt;

&lt;p&gt;The previous code was very simple, but one common principle in web development is the separation of concerns which we didn't really respect in the previous section with the two hooks.&lt;/p&gt;

&lt;p&gt;There are plenty of ways to make it done. For example, I will use a Provider component and the React contexts to handle this. You can find out more in my previous article &lt;a href="https://dev.to/brunosabot/how-i-dropped-redux-for-the-context-api-42po"&gt;How I dropped Redux for the Context API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To follow this way to separate concerns, I will now wrap the displaying component into another one that will manage the data fetching. Here's the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Our rendering code is now a bit cleaner since the logic has been extracted to another component in charge of the logic.&lt;/p&gt;

&lt;p&gt;You can see that I choose to use a loadData callback along with the useEffect hook this time. This is because I consider improving the method with additional parameters - not in this tutorial though - to manage pagination, revalidation, and more.&lt;/p&gt;

&lt;p&gt;In the same way, I have encapsulated the data inside a subobject &lt;code&gt;values&lt;/code&gt;, to be prepared to support another sub-object &lt;code&gt;actions&lt;/code&gt; for manual reload and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Loading and Error States
&lt;/h3&gt;

&lt;p&gt;In many applications, we want to show the user that we are currently loading the data or if we encounter an error.&lt;/p&gt;

&lt;p&gt;To do so, we just have to add two boolean states corresponding to the loading and the error.&lt;/p&gt;

&lt;p&gt;These states are meant to work this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default, the loading state should be false since there is no operation made&lt;/li&gt;
&lt;li&gt;As soon as we launch the data loading, the loading state should switch to true&lt;/li&gt;
&lt;li&gt;The loading state should get back to false as the request end&lt;/li&gt;
&lt;li&gt;By default, the error state should be false since there are no errors yet (and hopefully, ever)&lt;/li&gt;
&lt;li&gt;As soon as we launch the data loading, the error state should be reset to false to remove an older error&lt;/li&gt;
&lt;li&gt;The error state should switch to true if the loading goes wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a code sample:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, our application reports the loading and error states with a custom message to the user.&lt;/p&gt;

&lt;p&gt;This solution stays pretty basic, but you are free to add additional data, such as a specific message for the error, better loading, or a skeleton of the page to make an even better interface.&lt;/p&gt;

&lt;p&gt;A common mistake made on a lot of websites is to give no intel on what happened on a website. You can lose users because they think your app has crashed if there is no loading indicator, or they may think your service is simply not working if the error is not explicit enough.&lt;/p&gt;

&lt;p&gt;My personal recommendations are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a skeleton of your page while loading the data&lt;/li&gt;
&lt;li&gt;If possible, show a loading progress indicator&lt;/li&gt;
&lt;li&gt;If the first point is too complicated, add a spinner or a text indicating the data is loading&lt;/li&gt;
&lt;li&gt;Cache the request to avoid unnecessary waiting from the server or propose a "Stale While Revalidate" behavior&lt;/li&gt;
&lt;li&gt;If you encounter an error, give your user precise information on what is going on., e.g., "Your data hasn't been saved because it is not valid" or "We encountered a problem loading this product… Please try again later."&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Factorize to Hooks
&lt;/h3&gt;

&lt;p&gt;When developing an app, you will probably not have only one place where you will need to load data. Each one of your pages are candidates to fetch remote servers.&lt;/p&gt;

&lt;p&gt;With the previous code, we can clearly see a lot of code that will be copied if we want to keep the same code structure, even if the only update we want to make is an URL change.&lt;/p&gt;

&lt;p&gt;A good candidate to resolve this is to create a custom hook to contain the error, loading, and data state hook along with the data loading method. This hook will get an URL as a parameter, as shown below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Now, all the data fetching will be managed by the hook, and the provider code will be simpler to read.&lt;/p&gt;

&lt;p&gt;Once again, this is a pretty simple use case, you might need to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Making POST request&lt;/li&gt;
&lt;li&gt;Adding, on a POST request, a body content&lt;/li&gt;
&lt;li&gt;Handle HTTP headers&lt;/li&gt;
&lt;li&gt;Manage authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Do We Really Need the Separation Concern in a Component?
&lt;/h3&gt;

&lt;p&gt;Our provider became a simple pass-through from the hook to the component and we can ask ourselves if it is still a relevant component to include in our code or if it is unnecessary.&lt;/p&gt;

&lt;p&gt;I believe that the less component you have, the easier your code will be read by anyone else (validating the KISS principle). I choose then to remove the Provider part and only keep the view component and the hook. Here's the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Why have all these steps to get there? It is a pretty common mistake I saw in many projects to keep legacy code layers. I'm hoping that you will avoid these mistakes by seeing a complete rewrite of the code the more features you are adding to your data fetching.&lt;/p&gt;

&lt;p&gt;Depending on my needs, I can also remove the &lt;code&gt;useEffect&lt;/code&gt; part that could have been done here since we obviously always want to load the data straightaway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a Data Fetching Library
&lt;/h3&gt;

&lt;p&gt;Writing data fetching is very simple, but there are many reasons where coding all by yourself could become a huge pain. The preview code we just wrote could be easy to imagine in your mind, but what if you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a query caching system&lt;/li&gt;
&lt;li&gt;Handle an always up to date data&lt;/li&gt;
&lt;li&gt;Debug your requests&lt;/li&gt;
&lt;li&gt;Handle pagination and infinite loading&lt;/li&gt;
&lt;li&gt;Keep data available offline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Could you picture all the code required in your head right now? I personally can't, so I'm going to leave this to the greatest geniuses.&lt;/p&gt;

&lt;p&gt;So our requirements give us a lot of work, not even including the code maintenance, and the security patches that will be required. Hopefully, there are a few open source libraries that already manage this for you, such as &lt;a href="https://react-query.tanstack.com/"&gt;React Query&lt;/a&gt; and &lt;a href="https://swr.vercel.app/"&gt;SWR&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These libraries might be a (very little) bit more complicated to implement inside your apps than the hook we have previously coded, but they are also way more powerful.&lt;/p&gt;

&lt;p&gt;Let's see how we can start using them.&lt;/p&gt;

&lt;h3&gt;
  
  
  SWR
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://swr.vercel.app/"&gt;SWR&lt;/a&gt; is a lightweight library developed by &lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;SWR will however not handle the request itself. You will need to create a &lt;code&gt;fetcher&lt;/code&gt; method, but the code stays pretty straightforward, as you can see below:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Almost all the logic we previously wrote ourselves is managed by the useSWR hook. Don't think that the code magically disappeared!&lt;/p&gt;

&lt;p&gt;You might ask yourselves why should we use SWR if we still have to handle the &lt;code&gt;fetcher&lt;/code&gt; method? Because SWR has a lot of useful features including the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It automatically caches your requests&lt;/li&gt;
&lt;li&gt;It handles React suspense&lt;/li&gt;
&lt;li&gt;It automatically revalidates data when focusing the window and/or on regular intervals&lt;/li&gt;
&lt;li&gt;It can manage pagination, SSR&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  React Query
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://react-query.tanstack.com/"&gt;React Query&lt;/a&gt; is a little bit complicated to get started with: It will need a Provider component on the top of your application, combined with a query client.&lt;/p&gt;

&lt;p&gt;Also, like SWR, the actual fetching is yours to make.&lt;/p&gt;

&lt;p&gt;With that done, it will be as simple to use as everything we have covered so far, with only a different labelling system.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;React query also have a lot of awesome features you can check in comparison to other systems, available on the &lt;a href="https://react-query.tanstack.com/comparison"&gt;React Query website&lt;/a&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A powerful cache system&lt;/li&gt;
&lt;li&gt;Dedicated dev tools&lt;/li&gt;
&lt;li&gt;React Suspense support&lt;/li&gt;
&lt;li&gt;Auto-refreshing&lt;/li&gt;
&lt;li&gt;Pagination, SRR&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;There are plenty of ways to load data in React - from managing our own code to using powerful libraries.&lt;/p&gt;

&lt;p&gt;Personally, I would change the method I use depending on the size and nature of the project in the following conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When making a very small website with few requests, I will make my own fetching code (SWR and React Query came at a size cost)&lt;/li&gt;
&lt;li&gt;When the project gets bigger, I will go for SWR (Best size/features ratio)&lt;/li&gt;
&lt;li&gt;On big projects, I prefer to use React Query since it will cut me the work on many useful features (advanced features needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading. Please join me on &lt;a href="https://twitter.com/brunosabot"&gt;Twitter&lt;/a&gt; if you want to learn other things about React and more.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>softwareengineering</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Core Web Vitals Dashboard On Google Analytics</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Sun, 19 Dec 2021 09:04:18 +0000</pubDate>
      <link>https://dev.to/brunosabot/core-web-vitals-dashboard-on-google-analytics-5lk</link>
      <guid>https://dev.to/brunosabot/core-web-vitals-dashboard-on-google-analytics-5lk</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@mjessier"&gt;Myriam Jessier&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.dev/vitals/"&gt;Core Web Vitals&lt;/a&gt; are a set of well-known metrics used to measure your website performance. It’s often the entry point to investigating what is wrong with someone’s website and measure the progress made optimizing the overall speed and stability.&lt;/p&gt;

&lt;p&gt;Some of you (or your clients or employers) might not have the budget to invest into the various existing performance monitoring tools, or maybe no time to test and integrate them to the website, but they have a &lt;a href="https://analytics.google.com/analytics/web/"&gt;Google Analytics&lt;/a&gt; tracker set. With a few lines of code added, it is the perfect tool to start monitoring the performance experienced by the users when browsing the various pages of your web site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Including the tracking code
&lt;/h3&gt;

&lt;p&gt;In my example, I will be using &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; which is a React SSR (server-side rendering) framework with a lot of internal optimizations, plus wonderful components such as &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;Script&amp;gt;&lt;/code&gt; to improve the scripts and images loading and delaying/scheduling.&lt;/p&gt;

&lt;p&gt;To extract the tracking part, I first have a reusable &lt;code&gt;&amp;lt;Analytics&amp;gt;&lt;/code&gt; component which is responsible for including the two tags required by Google Analytics, and I’m using Next.js environment variables for the customization part.&lt;/p&gt;

&lt;p&gt;With the hypothesis I have in the first paragraph, you may not need this to be included since your client is likely to already use Google Analytics tracking, but you can use it on new projects or existing ones without tracking system.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Reporting Core Web Vitals with Next.js also needs to add/update the &lt;code&gt;_app.tsx&lt;/code&gt; file to add the metrics tracked and the page views tracked.&lt;/p&gt;

&lt;p&gt;Once again, here is the code if you do not already have Google Analytics on the project.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;There are two parts in the file to consider here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;reportWebVitals&lt;/code&gt; method that is automatically used by Next.js when it gets a Core Web Vital metric. If you are not using Next.js, you should look at the &lt;a href="https://www.npmjs.com/package/web-vitals"&gt;NPM web-vitals package&lt;/a&gt; to see the right method in your case.
a MyApp component that includes the &lt;code&gt;&amp;lt;Analytics&amp;gt;&lt;/code&gt; component presented earlier and a (bonus) hook to track the page change in Analytics. This one is to be used only if your client does not already have Google Analytics installed or you might duplicate the page change events sent to the server.
You now just need to add your UA value to the &lt;code&gt;.env&lt;/code&gt; file or as an environment variable &lt;code&gt;NEXT_PUBLIC_GOOGLE_ANALYTICS&lt;/code&gt; and you are ready!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Viewing data directly in Google Analytics
&lt;/h3&gt;

&lt;p&gt;After few days collecting, you will see the events (in green on the screenshot) appearing in Google Analytics in the Behavior (in blue) &amp;gt; Events (in purple) &amp;gt; Top Events (in red) &amp;gt; Event action (in orange) menu.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nWtzwCgT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://calendar.perfplanet.com/images/2021/bruno/view-events-on-analytics.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nWtzwCgT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://calendar.perfplanet.com/images/2021/bruno/view-events-on-analytics.png" alt="Viewing Core Web Vitals Events On Google Analytics" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And voilà, now you can report the real performance usage to your client directly in Google Analytics.&lt;/p&gt;

&lt;p&gt;Going further by creating custom dashboards&lt;br&gt;
If the previous screen give us the metrics, it is still a combined view where the chart shows all the metrics combined.&lt;/p&gt;

&lt;p&gt;To have a very specific view for LCP, FID and CLS, we can create custom dashboard by clicking on the Edit button on the top right od the screen.&lt;/p&gt;

&lt;p&gt;Once in the creation page, we need to update three block on the page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Dashboard title (in blue)&lt;/li&gt;
&lt;li&gt;The Tab name (in purple)&lt;/li&gt;
&lt;li&gt;The filters (in red), to Include / Event Action / LCP&lt;/li&gt;
&lt;li&gt;Then press the Save button and you are done for the first metric!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7_6mgwDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://calendar.perfplanet.com/images/2021/bruno/creating-lcp-dashboard-on-analytics.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7_6mgwDc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://calendar.perfplanet.com/images/2021/bruno/creating-lcp-dashboard-on-analytics.png" alt="Creating LCP Dashboard On Google Analytics" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You now just need to reproduce the previous steps for FID and CLS and you will have 3 custom reports with the Realtime User Monitoring (RUM) for your users!&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Adding dashboard for Core Web Vitals in Google Analytics is an easy way to get a Realtime User Monitoring of your website performance.&lt;/p&gt;

&lt;p&gt;If the created dashboard are too simple, you can go further thanks to Google Analytics power by adding second dimensions such as pages, device type, and even with conversion custom metrics.&lt;/p&gt;

&lt;p&gt;Happy monitoring!&lt;/p&gt;

</description>
      <category>corewebvitals</category>
      <category>performance</category>
      <category>nextjs</category>
      <category>googleanalytics</category>
    </item>
    <item>
      <title>Build an Easy Popup System With React</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Fri, 12 Nov 2021 17:16:47 +0000</pubDate>
      <link>https://dev.to/brunosabot/build-an-easy-popup-system-with-react-p5d</link>
      <guid>https://dev.to/brunosabot/build-an-easy-popup-system-with-react-p5d</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@windows"&gt;Windows&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Concerns About Existing Systems
&lt;/h3&gt;

&lt;p&gt;There are plenty of popup systems out there, but they usually don’t meet the high-quality requirement I have on user interfaces and development simplicity.&lt;/p&gt;

&lt;p&gt;When I’m adding a popup into a website, it is important to me that the system is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple to use&lt;/strong&gt;: as a developer, I don’t want to spend time creating tons of components and states just to activate a popup. A developer better spend their time on the domain specificities rather than brainless tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customisable&lt;/strong&gt;: this is usually my main point of complexity since popup systems are almost always shipped with styled components, making it - harder to make them look as close as your UI Designer has imagined them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessible&lt;/strong&gt;: Accessibility is usually created aside from the systems because it asks for more work, even if it doesn’t need that much work on it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these requirements, I always find it difficult to find a library with what I need and the blocking points are often too painful to be worked around.&lt;/p&gt;

&lt;p&gt;Even if it might not be intuitive, the last standing option is to create our own system so that will ensure a perfect match with your needs&lt;/p&gt;

&lt;p&gt;Enough speaking, let’s dive into a popup component system creation.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Are We Building
&lt;/h3&gt;

&lt;p&gt;There are a few things we want in this popup system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A custom modal component that will be in charge of the popup style, including background, position, and a closing button&lt;/li&gt;
&lt;li&gt;An easy-to-use modal component with a simple toggle system that will be in charge of the functional part of the popup.&lt;/li&gt;
&lt;li&gt;A changeable state to make the CSS modal softly appear&lt;/li&gt;
&lt;li&gt;Support for people who need a browser with reduced motion&lt;/li&gt;
&lt;li&gt;Handling accessibility on the modal to tell people with disabilities the popup has appeared and where to click so the popup will be closed&lt;/li&gt;
&lt;li&gt;A clickable background overlay to close the popup as we click out&lt;/li&gt;
&lt;li&gt;Handle the escape key to close the popup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s a lot to do so we better get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;p&gt;The first thing to have a modal system is to have a modal root, where the system will take place. To do so, we just need to have a new &lt;code&gt;div#modal-root&lt;/code&gt; element in our root document.&lt;/p&gt;

&lt;p&gt;This part is important so the modal can be easily styled. With a separate root element, we are sure that the parent elements of the modal does not have styles that will make it harder for us to reach the perfect style.&lt;/p&gt;

&lt;p&gt;To be sure that the modal will always be on top of the document, we just need to add the right &lt;code&gt;z-index&lt;/code&gt; on the application root and the modal-root.&lt;/p&gt;

&lt;p&gt;Also, since the modal behavior is to be opened and directly occupy the whole browser’s page, we add an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions"&gt;ARIA live region&lt;/a&gt; to the modal system so it can be announced to the user.&lt;/p&gt;

&lt;p&gt;The aria live region is set to assertive because we want the readers to have the same behavior as the browser, which places the popup on top of everything else.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  The modal components
&lt;/h3&gt;

&lt;p&gt;The modal component is split into three different components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;ModalPortal&lt;/code&gt; component that will link our modal to the &lt;code&gt;div#modal-root&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;ModalView&lt;/code&gt; component that aims to handle the visible part of the component&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;ModalAnimated&lt;/code&gt; component that will handle the popup domain and the CSS appearance effects of the popup system&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The ModalPortal component
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ModalPortal&lt;/code&gt; component exists to link our popup to the &lt;code&gt;div#modal-root&lt;/code&gt; element that we have created. Here’s the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;It is made of four sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;ref&lt;/code&gt; corresponding to a simple &lt;code&gt;div&lt;/code&gt; element, with the goal of holding the popup content. We do not use directly the root element so we are able to create two or more different popups if we want to stack them.&lt;/li&gt;
&lt;li&gt;A first &lt;code&gt;useEffect&lt;/code&gt; hook to create the &lt;code&gt;div&lt;/code&gt; element. This is a security to make the system work also on SSR systems such as NextJs or Gatsby.&lt;/li&gt;
&lt;li&gt;Another &lt;code&gt;useEffect&lt;/code&gt; hook, to add the previously created &lt;code&gt;div&lt;/code&gt; in the portal when active, and remove it when inactive. It will prevent the &lt;code&gt;div#modal-root&lt;/code&gt; element to contain plenty of empty divs.&lt;/li&gt;
&lt;li&gt;The render part, which is null if neither the &lt;code&gt;div&lt;/code&gt; element created does not exist or the popup is not currently active.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The ModalView component
&lt;/h4&gt;

&lt;p&gt;This one is basically a layout component so we can style the popup the way we want.&lt;/p&gt;

&lt;p&gt;Even if I’m presenting only one template, you are able to use it for as many needs you may have such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A popup system&lt;/li&gt;
&lt;li&gt;A designed replacement of the native &lt;code&gt;alert&lt;/code&gt; and &lt;code&gt;confirm&lt;/code&gt; modal&lt;/li&gt;
&lt;li&gt;A notification system&lt;/li&gt;
&lt;li&gt;Whatever else you can imagine&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The present component is just a bunch of native elements with some styles separated into two sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An overlay button, so the popup can be closed when clicking out&lt;/li&gt;
&lt;li&gt;The popup content itself, including a close button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The two blocks are siblings because we don’t want the click event to propagate from one to the other.&lt;/p&gt;

&lt;p&gt;For accessibility reasons, both the overlay and the close buttons are native button elements with an &lt;code&gt;aria-label&lt;/code&gt; attribute.&lt;/p&gt;

&lt;p&gt;In the CSS part, I use various positioning techniques that you are free to adapt depending on your needs.&lt;/p&gt;

&lt;h4&gt;
  
  
  The ModalAnimated component
&lt;/h4&gt;

&lt;p&gt;For the last part of the system, we need a component that will control the modal. Here’s the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This component has several tasks to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It has to load the ModalView component. By default, I chose to use the ModalView component, but I also give the component a prop to be able to change it&lt;/li&gt;
&lt;li&gt;It also has to manage the Modal portal component to include our content in the &lt;code&gt;div#modal-root&lt;/code&gt; DOM element&lt;/li&gt;
&lt;li&gt;It gives us access to an escape key support to close the modal.&lt;/li&gt;
&lt;li&gt;Finally, it handles a nice but optional transition effect.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The CSS has a weird CSS Modules syntax to handle global classes, but it also uses the &lt;code&gt;prefers-reduced-motion&lt;/code&gt; media query to shutdown the animation for people asking for it.&lt;/p&gt;

&lt;p&gt;If the last part could be set globally for all elements, it is better illustrated in the component.&lt;/p&gt;

&lt;h4&gt;
  
  
  The useEscape hook
&lt;/h4&gt;

&lt;p&gt;To improve usability, we can add another great feature to our popup system by adding an escape listener that can close the popup.&lt;/p&gt;

&lt;p&gt;To do so, there is a &lt;code&gt;useEscape(active, onClose);&lt;/code&gt; code in the ModalAnimated component, but this is yet to be implemented. Here’s the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The hook is quite simple, and it is made of two blocks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an &lt;code&gt;onEscape&lt;/code&gt; callback that memoize the keyboard event by listening to the keyCode for the escape key — 27&lt;/li&gt;
&lt;li&gt;an &lt;code&gt;useEffect&lt;/code&gt; method to bind it to the window document and unbind it as soon as the modal is unmounted&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The usage
&lt;/h4&gt;

&lt;p&gt;The usage is pretty straightforward: we need the &lt;code&gt;ModalAnimated&lt;/code&gt; component with two props if we want a custom ModalView component.&lt;/p&gt;

&lt;p&gt;The content of the popup itself is just the children elements passed to &lt;code&gt;ModalAnimated&lt;/code&gt;. I usually put the content inside another component to keep the page as light as possible. Here’s the code:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;By creating three light components and a simple custom hook, we are able to get a very modulable and customizable popup system.&lt;/p&gt;

&lt;p&gt;While it can still be improved, we have implemented a system that will make your UI designer happy, and it implements the accessibility basics.&lt;/p&gt;

&lt;p&gt;Did we check all the initial requirements?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple to use: yes&lt;/li&gt;
&lt;li&gt;Customisable: we can customise the view very easily&lt;/li&gt;
&lt;li&gt;Accessible: We do have a11y included in the code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mission accomplished! Now it is your turn to use it and improve it in your projects.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A Discord bot for Home Assistant</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Fri, 05 Nov 2021 21:28:54 +0000</pubDate>
      <link>https://dev.to/brunosabot/a-discord-bot-for-home-assistant-4nj3</link>
      <guid>https://dev.to/brunosabot/a-discord-bot-for-home-assistant-4nj3</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@linuscodes" rel="noopener noreferrer"&gt;Linus Rogge&lt;/a&gt; on &lt;a href="https://unsplash.com" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A bit of context
&lt;/h3&gt;

&lt;p&gt;I’ve been using &lt;a href="https://www.home-assistant.io/" rel="noopener noreferrer"&gt;Home Assistant&lt;/a&gt; for a while to manage my connected home. Home Assistant or &lt;em&gt;Hass&lt;/em&gt; is a Python based application I have installed on a Raspberry Pi and which is able to connect. to many services from &lt;em&gt;Google Assistant&lt;/em&gt;, &lt;em&gt;Philips Hue&lt;/em&gt;, &lt;em&gt;The Zigbee Protocol&lt;/em&gt;, and many others -1857 at the time I’m writing this post-.&lt;/p&gt;

&lt;p&gt;The software gaves us a perfect way to organise our homes, but I needed a powerful system that can allow me to get notified and request the instance from outside my home. Basically, the usages are :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Getting notified I left my home without setting my alarm up, then being able to activated the alarm&lt;/li&gt;
&lt;li&gt;Asking a room temperature to activate or deactivate the heater&lt;/li&gt;
&lt;li&gt;Turning on and off lights&lt;/li&gt;
&lt;li&gt;Knowing when it is time to get the trash out for the morning collect&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many ways to do this with Home Assistant, but I needed something that gets my attention quickly: when working on my laptop, I barely look at my phone. When I’m out, I generally don’t have access to my laptop. Moreover, I need something that is capable to notify all my family when something has to be done, especially when it is as important as setting the home security alarm.&lt;/p&gt;

&lt;p&gt;I made many tests of many Home Assistant integrations, and finally the one that checked all my requirements was Discord -which I’m using for many other things-. Since some points where not trivia, here is a step by step guide to set and get data between a Discord instance and a Home Assistant instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Software and requirements
&lt;/h3&gt;

&lt;p&gt;First of all, I need a Home Assistant instance, which in my case is installed on a &lt;a href="https://www.raspberrypi.org/" rel="noopener noreferrer"&gt;Raspberry Pi&lt;/a&gt;. I will not explain in details how to install it right here since the tutorial on the website are good enough. Also, I think you should play around with your instance before considering connecting it with Discord. To make an installation on your own, you can head to &lt;a href="https://www.home-assistant.io/installation/" rel="noopener noreferrer"&gt;the documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creation of a Discord Bot
&lt;/h3&gt;

&lt;p&gt;To create a bot, you need to go to the &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord developer dashboard&lt;/a&gt; and click on the &lt;em&gt;New Application&lt;/em&gt; button to create your bot. Choose a name, &lt;em&gt;Home Assistant&lt;/em&gt; for example, and click &lt;em&gt;Create&lt;/em&gt; .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_0Zz1E4924H7u7ChJsANZqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_0Zz1E4924H7u7ChJsANZqg.png" alt="Use the purple “New Application” button"&gt;&lt;/a&gt;&lt;br&gt;Use the purple “New Application” button
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_ac-MpDUvhoJE4JUXsGhDrg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_ac-MpDUvhoJE4JUXsGhDrg.png" alt="Add the application name and click Create"&gt;&lt;/a&gt;&lt;br&gt;Add the application name and click Create
  &lt;/p&gt;

&lt;p&gt;In the application screen that follows next, feel free to change the name, the application icon or to fill the application description, then head to the &lt;em&gt;OAuth2&lt;/em&gt; menu on the left.&lt;/p&gt;

&lt;p&gt;In this menu, you need to get and save for later your App’s &lt;em&gt;client ID&lt;/em&gt; . This ID will be needed later to activate the bot on your server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_i-vohVPcDmImtYijOAMtmg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_i-vohVPcDmImtYijOAMtmg.png" alt="Gather your client ID for later usage"&gt;&lt;/a&gt;&lt;br&gt;Gather your client ID for later usage
  &lt;/p&gt;

&lt;p&gt;Now you have your Client ID, we can head to the Bot menu to make the final configuration step of our application.&lt;/p&gt;

&lt;p&gt;Once you are on the page, just click on the &lt;em&gt;Add bot&lt;/em&gt; button and confirm the bot creation after having carefully read the consequences on the confirmation popup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_ot9Na96nJhxfBgbF_V7mtg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_ot9Na96nJhxfBgbF_V7mtg.png" alt="Click the left “Bot” menu, then click the “Add bot” button"&gt;&lt;/a&gt;&lt;br&gt;Click the left “Bot” menu, then click the “Add bot” button
  &lt;/p&gt;

&lt;p&gt;In the newly created bot, you will be able to customise its name and its picture, but the most important part is to gather the bot token and keep it somewhere. You should so far have saved two things : the application &lt;em&gt;Client ID&lt;/em&gt; and this &lt;em&gt;Token&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Keep in mind that this token needs to be absolutely secret.&lt;br&gt;
&lt;strong&gt;Anyone that get yours will be able to collect and send messages on behalf of your bot.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_eaUdfCIte-eC07lYp4gsGg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_eaUdfCIte-eC07lYp4gsGg.png" alt="Click on “Copy” to get your bot token that will be used later, in Node Red"&gt;&lt;/a&gt;&lt;br&gt;Click on “Copy” to get your bot token that will be used later, in Node Red
  &lt;/p&gt;

&lt;p&gt;The final step is to activate your bot on your server. You only need to head to the following URL. Don’t forget to change the placeholder value with your previously recovered Client ID :&lt;/p&gt;

&lt;pre&gt;
https://discord.com/oauth2/authorize?scope=bot&amp;amp;permissions=0&amp;amp;client_id=REPLACE_WITH_YOUR_CLIENT_ID
&lt;/pre&gt;

&lt;p&gt;Once the validation window checked the pop will jump on your server, and you will officially be done with this part.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_zSDdv_ZHnTsKIwii6uYJoQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_zSDdv_ZHnTsKIwii6uYJoQ.png" alt="Select a server then Authorize it and your bot will jump in."&gt;&lt;/a&gt;&lt;br&gt;Select a server then Authorize it and your bot will jump in.
  &lt;/p&gt;

&lt;h3&gt;
  
  
  The flow creation
&lt;/h3&gt;

&lt;p&gt;The next part of the configuration will take place in the Node Red module on Home Assistant. Once again, I will consider now that you know the software enough to understand the specific notions I might use.&lt;/p&gt;

&lt;p&gt;Now that I’m considering you are correctly set up, we are going to install a required module: [Node-Red](&lt;a href="https://nodered.org/" rel="noopener noreferrer"&gt;https://nodered.org/&lt;/a&gt;. To do so, go to the &lt;em&gt;Supervisor&lt;/em&gt; menu, choose the &lt;em&gt;Add-ons&lt;/em&gt; tab, search &lt;em&gt;node-red&lt;/em&gt; and install it. I will take few minutes before you can head up to the add-on &lt;em&gt;configuration&lt;/em&gt; tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_IWDMy6N7TjWtb2v5CpR_hQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_IWDMy6N7TjWtb2v5CpR_hQ.png" alt="The Node RED add-on on Home Assistant"&gt;&lt;/a&gt;&lt;br&gt;The Node RED add-on on Home Assistant
  &lt;/p&gt;

&lt;p&gt;In the configuration tab, set at least a &lt;em&gt;credential_secret&lt;/em&gt; to protec your data, then get back to info and start the add-on. I personally choose to activate the menu UI, so I can access it directly from the left menu, and the watchdog but it is a choice up to you.&lt;/p&gt;

&lt;p&gt;After few seconds, the add-on is ready and so we are! Click on the UI link, in the menu or in the &lt;em&gt;info&lt;/em&gt; tab from the add-on to get to Node-RED.&lt;/p&gt;

&lt;p&gt;Node RED come with a lot of integrated plugins. However, the Discord plugin is not installed by default. You need to use the &lt;em&gt;Settings&lt;/em&gt; menu to activate it.&lt;/p&gt;

&lt;p&gt;Plugins are located in the Palette tab. Search for &lt;em&gt;node-red-contrib-discord&lt;/em&gt; and install it. Some other plugins might work, but it will be up to you to test them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_HxNwyHJQW4Uhbz0i4lw2fw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_HxNwyHJQW4Uhbz0i4lw2fw.png"&gt;&lt;/a&gt;&lt;br&gt;The Setting menu
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_FpsD0o0m2nXZD2qlkqck0Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_FpsD0o0m2nXZD2qlkqck0Q.png" alt="The node-red-contrib-discord plugin"&gt;&lt;/a&gt;&lt;br&gt;The node-red-contrib-discord plugin
  &lt;/p&gt;

&lt;h4&gt;
  
  
  The base workflow
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_fSFOZLyZ_VdpOexxRnOTIQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_fSFOZLyZ_VdpOexxRnOTIQ.png" alt="The base workflow"&gt;&lt;/a&gt;&lt;br&gt;The base workflow
  &lt;/p&gt;

&lt;p&gt;The base workflow is made with four blocks :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;discordMessage&lt;/strong&gt;: listen to messages coming from Discord. You should use wisely in which channels your Bot is, since it will listen for everything. This block is configurable with a Discord token, the one you saved earlier in the Discord configuration. I recommend you add a specific role for your bot, and add him in very specific channels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;switch&lt;/strong&gt;: controls the user rights. I basically use it to check if the sending user have the proper role. To do so, I make sure the &lt;em&gt;msg.memberRoleNames&lt;/em&gt; contains my Discord role. This is very useful because I have a lot of people on my server, and I of course don’t want any of them to be able to interact with my home or get sensitive data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;change&lt;/strong&gt;: update and parse my data to be handled somewhere else. I chose to make my commands on a 3 words basis (namespace, command, value). To do so, the change allows me to change the payload with a parsed value. It is pretty basic, but if you need something smarter, you can use the &lt;em&gt;function&lt;/em&gt; block where you can write plain JavaScript.
&lt;strong&gt;switch&lt;/strong&gt;: get the namespace from the payload and head to the requested flow. It might contains a lot of different outputs. I only got four for now (light, temperature, humidity and help)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can check the config I set in every of these blocks below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_QlWM5kd5ZRgCnDTb1icf-Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_QlWM5kd5ZRgCnDTb1icf-Q.png" alt="The discordMessage block configuration. Use the pencil button to add"&gt;&lt;/a&gt;&lt;br&gt;The discordMessage block configuration. Use the pencil button to add  another token&lt;br&gt;

  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1__FKVFxxt6H__hnat79Gusw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1__FKVFxxt6H__hnat79Gusw.png" alt="The switch block configuration. Stewjon -Star Wars fan will"&gt;&lt;/a&gt;&lt;br&gt;The switch block configuration. Stewjon -Star Wars fan will  understand- is the role name from Discord
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_VcJMpHBJFAgq1xXaLICXQw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_VcJMpHBJFAgq1xXaLICXQw.png" alt="Lowercase, trim and split the playload for my use cases"&gt;&lt;/a&gt;&lt;br&gt;Lowercase, trim and split the playload for my use cases
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_Oo1xwQ32t6utbJ8rV1NE9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_Oo1xwQ32t6utbJ8rV1NE9g.png" alt="Switch to the right flow depending on the requested namespace"&gt;&lt;/a&gt;&lt;br&gt;Switch to the right flow depending on the requested namespace
  &lt;/p&gt;

&lt;h4&gt;
  
  
  First flow: Calling a service
&lt;/h4&gt;

&lt;p&gt;The first scheme I have made is something that allows me to turn on and off lights directly from Discord. It is made of three basic steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_Wd9WY2Hoa9F91TwsTIwNgw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_Wd9WY2Hoa9F91TwsTIwNgw.png" alt="The light management on Discord"&gt;&lt;/a&gt;&lt;br&gt;The light management on Discord
  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;change&lt;/strong&gt;: This first block is aimed to make a translation between a word and Home Assistant entity. I need to transform a sentence lik &lt;em&gt;lumière bureau on _ into a call to the proper service. Basically, it means converting _lumière&lt;/em&gt; to &lt;em&gt;light (namespace), bureau&lt;/em&gt; to &lt;em&gt;light.lumieres_du_bureau&lt;/em&gt; (entity) and &lt;em&gt;on&lt;/em&gt; into &lt;em&gt;turn_on&lt;/em&gt; (some words are in french to be easier to understand for my family). Like I suggest on the base workflow, you might want to switch this block to a &lt;em&gt;function&lt;/em&gt; block to write plain JavaScript, which I will do someday.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;template&lt;/strong&gt;: This second block convert a Node Red data into a proper Home Assistant Data, which is an object composed with three keys: &lt;em&gt;domain&lt;/em&gt;, &lt;em&gt;service&lt;/em&gt; and &lt;em&gt;data&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;call service&lt;/strong&gt;: This is the block that link our Node Red workflow with Home Assistant and make the proper call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_x-SPnVHz2XlXBHBUQ0iGLA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_x-SPnVHz2XlXBHBUQ0iGLA.png" alt="First scheme first block: changing Discord data into a Node"&gt;&lt;/a&gt;&lt;br&gt;First scheme first block: changing Discord data into a Node  Red payload&lt;br&gt;

  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_WHWMrOssUNdDka2NGeMmmg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_WHWMrOssUNdDka2NGeMmmg.png" alt="Second block: Formating the Node Red workflow for a Discord"&gt;&lt;/a&gt;&lt;br&gt;Second block: Formating the Node Red workflow for a Discord  service call&lt;br&gt;

  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_KOuniuzjolNRAtAInYpjXA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_KOuniuzjolNRAtAInYpjXA.png" alt="Last block: Calling the Home Assistant service"&gt;&lt;/a&gt;&lt;br&gt;Last block: Calling the Home Assistant service
  &lt;/p&gt;

&lt;h4&gt;
  
  
  Second scheme: Getting a sensor data
&lt;/h4&gt;

&lt;p&gt;I also want to be able to recover informations from a sensor. I have currently implemented two of them, temperature and humidity.&lt;/p&gt;

&lt;p&gt;I will only present the temperature since the behaviour will be exactly&lt;br&gt;
the same for humidity, just the “Get entity value” content will slightly&lt;br&gt;
differ.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_am7lKaocgozwgeu2bfsonA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_am7lKaocgozwgeu2bfsonA.png" alt="Temperature recovery from Discord"&gt;&lt;/a&gt;&lt;br&gt;Temperature recovery from Discord
  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;change&lt;/strong&gt;: Same as the previous scheme, allow to convert a word to the proper entity name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;current state&lt;/strong&gt;: This allow Node Red to recover data directly from Home Assistant then continue the flow with the value provided&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;template&lt;/strong&gt;: Template is this time used to format the data to be sent in Discord.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;discordSendMessage&lt;/strong&gt;: Making the actual call to Discord. A new message from the bot will appear in the proper channel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_ON8jVlKrZlrxYQjG3QphUg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_ON8jVlKrZlrxYQjG3QphUg.png" alt="First scheme first block: changing Discord data into a Node"&gt;&lt;/a&gt;&lt;br&gt;First scheme first block: changing Discord data into a Node  Red payload
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_cLTTMkHgc0qnqEtJaQ_gnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_cLTTMkHgc0qnqEtJaQ_gnw.png" alt="Recovering data from Home Assistant"&gt;&lt;/a&gt;&lt;br&gt;Recovering data from Home Assistant
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_Bk1fE5NPJFe4IKVCwJg5dQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_Bk1fE5NPJFe4IKVCwJg5dQ.png" alt="Simple message formatting"&gt;&lt;/a&gt;&lt;br&gt;Simple message formatting
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_XNF2UJfyRrFaFFuPCiJquA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_XNF2UJfyRrFaFFuPCiJquA.png" alt="Sending the message in the same channel we got the incoming message"&gt;&lt;/a&gt;&lt;br&gt;Sending the message in the same channel we got the incoming message
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_3zQiTWObeD0wS-7PP8A8dQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_3zQiTWObeD0wS-7PP8A8dQ.png" alt="Result shown in Discord"&gt;&lt;/a&gt;&lt;br&gt;Result shown in Discord
  &lt;/p&gt;

&lt;h4&gt;
  
  
  Third scheme: A CLI-like help
&lt;/h4&gt;

&lt;p&gt;Finally, I want to be able to show an help message to whoever can use Home Assistant.&lt;/p&gt;

&lt;p&gt;The system is just one big switch we are going to see together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_e2tmco9dyZweKepD1K3N4w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_e2tmco9dyZweKepD1K3N4w.png" alt="CLI-like help workflow"&gt;&lt;/a&gt;&lt;br&gt;CLI-like help workflow
  &lt;/p&gt;

&lt;p&gt;To do so, we are going to need only three types of nodes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;switch&lt;/strong&gt;: This node will make the choice of the right template depending on the help type : light, humidity, temperate, and, if there is no type given, the default help.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;template&lt;/strong&gt;: There are a bunch of templates. Each one is just plain text corresponding to the help I want to show on screen&lt;/li&gt;
&lt;li&gt;**discordSendMessage: Sending the message to the Discord server so it may looks like the bot is answering me.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_oIc9sryQvGaX0tfxt_CQqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_oIc9sryQvGaX0tfxt_CQqg.png" alt="Switching to a default help or a specific one"&gt;&lt;/a&gt;&lt;br&gt;Switching to a default help or a specific one
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_gxReqCUM7iA2sraTHGxLVw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_gxReqCUM7iA2sraTHGxLVw.png" alt="An help template example"&gt;&lt;/a&gt;&lt;br&gt;An help template example
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_MueLiDyLRzPTSEqItj9GhQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fbrunosabot.dev%2Fimg%2F1_MueLiDyLRzPTSEqItj9GhQ.png" alt="Finally sending the message to Discord"&gt;&lt;/a&gt;&lt;br&gt;Finally sending the message to Discord
  &lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Home assistant is a very powerful tool and combined with Node Red, it can be absolutely amazing.&lt;/p&gt;

&lt;p&gt;The examples I have presented here are very simple on purpose, but it can be enhanced to take your Home Assistant to an higher level. I might share other posts later to explain some other workflow I had implemented.&lt;/p&gt;

&lt;p&gt;I hope you’ll get closer to the perfect home automation thanks to this tutorial&lt;/p&gt;

</description>
      <category>discord</category>
      <category>homeassistant</category>
      <category>bot</category>
      <category>homeautomation</category>
    </item>
    <item>
      <title>My Journey From React to React Native</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Mon, 21 Sep 2020 14:32:03 +0000</pubDate>
      <link>https://dev.to/brunosabot/my-journey-from-react-to-react-native-p44</link>
      <guid>https://dev.to/brunosabot/my-journey-from-react-to-react-native-p44</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@mukukostudio"&gt;Mukuko Studio&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’ve recently started working on an Android application, and as a React developer, I made the easy choice to use and test &lt;a href="https://reactnative.dev/"&gt;React Native&lt;/a&gt; to do so because it helped me stay in my comfort zone and also gives me the opportunity to explore iOS someday.&lt;/p&gt;

&lt;p&gt;Even if it is the same framework, using React for native applications is a little bit different than React on the web.&lt;/p&gt;

&lt;p&gt;I’m writing this article to share the main differences I have found between the two platforms along with a few tips I had to figure out to obtain the desired final behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  View or Text — There Is No div
&lt;/h3&gt;

&lt;p&gt;When working on a web application, we mostly use &lt;code&gt;div&lt;/code&gt; and &lt;code&gt;span&lt;/code&gt; tags for many usages. Since we are not on the web, this is no longer a possibility.&lt;/p&gt;

&lt;p&gt;Instead, the content is made with &lt;code&gt;View&lt;/code&gt; and &lt;code&gt;Text&lt;/code&gt; that we could associate with the two tags above, but they have some additional constraints.&lt;/p&gt;

&lt;h4&gt;
  
  
  The View element
&lt;/h4&gt;

&lt;p&gt;With the &lt;code&gt;View&lt;/code&gt; element, you can’t add anything else inside other than components. That means it cannot contain text, which the &lt;code&gt;Text&lt;/code&gt; component is for. As an unfortunate consequence, it has a larger tree in your application, but it helps to separate concerns between layout and text.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Hello world component in React Native




&lt;p&gt;Based on the previous point, you can easily figure out that you can’t apply text-related styles to a &lt;code&gt;View&lt;/code&gt; component. The text styles like &lt;code&gt;color&lt;/code&gt; or &lt;code&gt;fontSize&lt;/code&gt; need to be applied to the &lt;code&gt;Text&lt;/code&gt; component.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Layout styles on View, text styles on Text




&lt;p&gt;&lt;code&gt;View&lt;/code&gt; is also a &lt;em&gt;flexbox&lt;/em&gt; container that can only support two display values: &lt;code&gt;none&lt;/code&gt; or &lt;code&gt;flex&lt;/code&gt;. It can change numerous things if you are not confident with the model, but it is much more powerful than the classic block model used by default on the DOM.&lt;/p&gt;

&lt;p&gt;You can learn more about this layout system on &lt;a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/"&gt;CSS-Tricks&lt;/a&gt;. Every flex property is supported in React Native, from &lt;code&gt;align-items&lt;/code&gt; to &lt;code&gt;flex-grow&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There is, however, one major difference between the web version and the native version: the default value of &lt;code&gt;flex-direction&lt;/code&gt;. If we have &lt;code&gt;row&lt;/code&gt; on the web, it is set to &lt;code&gt;column&lt;/code&gt; in React Native. Basically, this means that elements are placed by default from top to bottom instead of left to right.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Flexbox usage on React Native




&lt;p&gt;Finally, &lt;code&gt;View&lt;/code&gt; is not clickable. If you need a click behavior on it, you’ll have to wrap it into a &lt;code&gt;Touchable*&lt;/code&gt; component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TouchableHighlight&lt;/code&gt; to add a background color on click.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TouchableOpacity&lt;/code&gt; to reduce opacity on click.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TouchableWithoutFeedback&lt;/code&gt; to have no feedback on click, which I don’t recommend for user experience reasons.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TouchableNativeFeedback&lt;/code&gt; (only on Android) to have the ripple effect on the button.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Example usage of TouchableHighlight




&lt;h4&gt;
  
  
  The Text element
&lt;/h4&gt;

&lt;p&gt;If we can easily compare the &lt;code&gt;Text&lt;/code&gt; element to a &lt;code&gt;span&lt;/code&gt; tag on the web, the difference is as noticeable as with views.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Text&lt;/code&gt; element — as it is aptly named — exists only to make the rendering of text contents. We cannot use it for any layout-related stuff we might need. Therefore, &lt;code&gt;display: "flex"&lt;/code&gt; will have no effect. Neither will &lt;code&gt;position&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, the &lt;code&gt;Text&lt;/code&gt; inherits styles from the parent &lt;code&gt;Text&lt;/code&gt; component like it does on the web.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Text component style inheritance




&lt;p&gt;Like &lt;code&gt;View&lt;/code&gt;, the &lt;code&gt;Text&lt;/code&gt; component is not clickable. If that’s a behavior you need, you will have to wrap into one of the &lt;code&gt;Touchable*&lt;/code&gt; components.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;Text&lt;/code&gt; is only meant to contain text and other &lt;code&gt;Text&lt;/code&gt; components. You should not include layout-related components like &lt;code&gt;View&lt;/code&gt;, &lt;code&gt;ScrollView&lt;/code&gt;, or &lt;code&gt;FlatList&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replace Input With TextInput
&lt;/h3&gt;

&lt;p&gt;Since the Native API is not DOM, we do not have &lt;code&gt;input&lt;/code&gt; elements either, but React provides a component for the times when we need a form.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;InputField&lt;/code&gt; component works the same as &lt;code&gt;input&lt;/code&gt; but also has a &lt;code&gt;onChangeText&lt;/code&gt; attribute that accepts a callback with the value as an argument. No more need for &lt;code&gt;event.target.value&lt;/code&gt;!&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
TextInput and the onChangeText callback




&lt;h3&gt;
  
  
  The CSS Usage
&lt;/h3&gt;

&lt;p&gt;If I’m using &lt;a href="https://github.com/css-modules/css-modules"&gt;CSS Modules&lt;/a&gt; when I’m working on a web application, it is a bit different on native, where the CSS usage is more the CSS-in-JS way. The stylesheets are created with the &lt;code&gt;StyleSheet.create&lt;/code&gt; method that is provided by React Native and is a key/value object of class/styles for the component.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Styling with StyleSheet.create()




&lt;p&gt;If there are units in CSS, there are not in React Native — or more precisely, units are always set in &lt;code&gt;dp&lt;/code&gt;, so the render will be right even if the phones do not all have the same pixel ratio. It makes the CSS usage a bit different, but if you want to make things simpler, just consider them pixels.&lt;/p&gt;

&lt;p&gt;If we used to have shortcuts in CSS, it is not the same in React Native: &lt;code&gt;padding&lt;/code&gt; must be a number and not a list of values in a string, &lt;code&gt;backgroundColor&lt;/code&gt; is used for the color, and so on.&lt;/p&gt;

&lt;p&gt;To illustrate that rule, assume that the CSS &lt;code&gt;padding: "8 16"&lt;/code&gt; is not valid, and so &lt;code&gt;background: "#333333"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Even if these are a bit longer to type in, I find it way more explicit than the shortcuts we are used to. Plus, they are always complicated to understand for a beginner.&lt;/p&gt;

&lt;p&gt;After a couple of hours, I had definitely adopted this new way of writing CSS.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
dp units and shortcuts




&lt;h3&gt;
  
  
  Scalable Vector Graphics
&lt;/h3&gt;

&lt;p&gt;If SVG is used a lot on the web, it is not natively supported in React Native. We need to use it with an external package: &lt;code&gt;react-native-svg&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, the package is made to be used exactly like on the web with just a little difference: the first uppercase character.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Simple SVG in React Native




&lt;h3&gt;
  
  
  Overflow
&lt;/h3&gt;

&lt;p&gt;If you want a scrollable &lt;code&gt;View&lt;/code&gt;, you need to switch to the &lt;code&gt;ScrollView&lt;/code&gt; component. It acts the same but has a scrollbar built in.&lt;/p&gt;

&lt;p&gt;Since the component has a vertical scroll by default, you can use the &lt;code&gt;horizontal&lt;/code&gt; attribute to make it scroll on the &lt;em&gt;x&lt;/em&gt;-axis.&lt;/p&gt;

&lt;p&gt;For performance reasons, you can also use the&lt;code&gt;FlatList&lt;/code&gt; component, which is a bit more complicated to use, but it will make your long lists scroll fast. If it is something you need, I encourage you to &lt;a href="https://reactnative.dev/docs/flatlist"&gt;look at the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tips and Tricks
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Touchable components are applied to a single element
&lt;/h4&gt;

&lt;p&gt;If you get the error &lt;code&gt;Error: React.Children.only expected to receive a single React element child&lt;/code&gt;, then you just need to wrap your elements in a new &lt;code&gt;View&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;It seems pretty obvious what to do, but it can be a bit disturbing when coming from the web: When using &lt;code&gt;Touchable*&lt;/code&gt; components, you need to have a single layout item.&lt;/p&gt;

&lt;h4&gt;
  
  
  Line breaks in &lt;code&gt;Text&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;On the web, new lines are made with &lt;code&gt;&amp;lt;br /&amp;gt;&lt;/code&gt;, but since native is not DOM, you can simply use &lt;code&gt;{"\n"}&lt;/code&gt; in your &lt;code&gt;Text&lt;/code&gt; components or directly in a string (e.g. &lt;code&gt;&amp;lt;Text&amp;gt;{"Hello\nworld!"}&amp;lt;/Text&amp;gt;&lt;/code&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  Views in Text
&lt;/h4&gt;

&lt;p&gt;You cannot have &lt;code&gt;View&lt;/code&gt; elements in &lt;code&gt;Text&lt;/code&gt; elements. This throws the following error: &lt;code&gt;Cannot add a child that doesn't have a YogaNode to a parent without a measure function!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It might make your tree a bit more complex with some code duplication, but you should always find a way to avoid this message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Even though React Native is based on React, there are plenty of differences. On one hand, we use React on the web and use the DOM API. On the other hand, we use the native layouts for Android, iOS, and others. But it is still very easy to get into. Do not hesitate to give it a try!&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>mobile</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>An Opinionated Way to Structure React Apps</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Tue, 05 May 2020 02:34:22 +0000</pubDate>
      <link>https://dev.to/brunosabot/an-opinionated-way-to-structure-react-apps-2bkg</link>
      <guid>https://dev.to/brunosabot/an-opinionated-way-to-structure-react-apps-2bkg</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@dtopkin1"&gt;Dayne Topkin&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When we first develop a React app, we can just put every component in a folder and it works. But when it comes to larger projects, it might be difficult to find our way between files if we keep using React this way.&lt;/p&gt;

&lt;p&gt;So how can we handle a bigger project? &lt;a href="https://react-file-structure.surge.sh/"&gt;Dan Abramov has a way&lt;/a&gt;. You don’t think this is very helpful? Actually, it is. It’s the best way to find the perfect architecture that will fit your needs, but at a cost of many iterations in folder creation and removal.&lt;/p&gt;

&lt;p&gt;Today, I’m introducing the result of my many moves, making a base structure for people seeking a way to improve their own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial Considerations
&lt;/h3&gt;

&lt;p&gt;Before we start, I’d like to point out that I’m presenting an opinionated way to structure an app. In some projects, I had to do things differently because the app’s core concept was too different. It might also be the case for you.&lt;/p&gt;

&lt;p&gt;Also, there are several ideas I’d like to introduce so you will better understand the why.&lt;/p&gt;

&lt;p&gt;First of all, I use &lt;a href="https://atomicdesign.bradfrost.com/"&gt;atomic design&lt;/a&gt;. Some components are only visual. Basically, it concerns every component that will end up in my &lt;a href="https://storybook.js.org/"&gt;Storybook&lt;/a&gt;. I call them ui components. Atomic design also brings template components.&lt;/p&gt;

&lt;p&gt;Some other components are given a specific behavior to a form field, like an enhanced form field that gives a validation pattern to a browser default form field. They are the organisms within atomic design.&lt;/p&gt;

&lt;p&gt;Finally, I’m using the React Context API instead of redux, as I &lt;a href="https://medium.com/@brunosabot/how-i-dropped-redux-for-the-context-api-7338d481e179"&gt;explained in one of my previous posts&lt;/a&gt;. I create top-level components that I call providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started With the Root Folder
&lt;/h3&gt;

&lt;p&gt;Working with &lt;a href="https://create-react-app.dev/"&gt;create-react-app&lt;/a&gt;, the root folder of my application is the &lt;code&gt;src&lt;/code&gt; folder in which I place several folders:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;App&lt;/code&gt; — The folder where the main component is placed containing global providers and main routing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;components&lt;/code&gt; — Where every React component of the application belongs.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gql&lt;/code&gt; — In which I can find every piece of a GraphQL request I can make in my application.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;libs&lt;/code&gt; — This is a bit of a mess, but it contains everything else. It is generally composed of fewer than ten files, so I never had to split them better.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the better ratio I found between simplicity and code splitting for the base structure. Since React is a component framework, you can easily imagine that the &lt;code&gt;components&lt;/code&gt; folder will be a bit more complex.&lt;/p&gt;

&lt;p&gt;I will not explain in detail the three other folders. You can have a look at the sample tree at the bottom of this post to find out more about the kind of files placed in there.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Components Folder
&lt;/h3&gt;

&lt;p&gt;Here we are: the main part of the application. This one is composed of many more subfolders. Keep in mind that if you copy this structure, you do not need to absolutely use them all if it doesn’t make sense in your project. For example, the &lt;code&gt;ui&lt;/code&gt; folder doesn’t make sense in a &lt;a href="https://material-ui.com/"&gt;Material-UI&lt;/a&gt; application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hooks&lt;/code&gt;— Where I place a good amount of the hooks I use in my app. I have a lot of them to embrace the power of reusability, so I also create subfolders to illustrate the job they belong to. For example, I often have a &lt;code&gt;useInterval&lt;/code&gt; hook to handle cyclic jobs. I also place in there a &lt;code&gt;useUser&lt;/code&gt; hook that gives me the current connected user information.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;modals&lt;/code&gt; — This regroups every modal in my project. I used to place them elsewhere, but I actually found that I often use them many times in the application, and they are quite numerous. By having their own folder, it became simpler for me to work on them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;organisms&lt;/code&gt; — The folder where I place the functional components I spoke about earlier. It can be split into subfolders if there are too many of them, which happens a lot.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;providers&lt;/code&gt; — Components that contain global data or feature logic. To find out more about what a provider looks like, I invite you to take a look &lt;a href="https://medium.com/better-programming/how-i-dropped-redux-for-the-context-api-7338d481e179"&gt;at a previous post&lt;/a&gt; where I replace redux with them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;svg&lt;/code&gt; — The home of every icon used in the application since create-react-app &lt;a href="https://create-react-app.dev/docs/adding-images-fonts-and-files/#adding-svgs"&gt;can include them natively&lt;/a&gt;. You might have a designer, but in case you don’t, I really love the &lt;a href="https://materialdesignicons.com/"&gt;Material Design Iconset&lt;/a&gt;, where I can always find the perfect icon for my apps.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;templates&lt;/code&gt; — In which I have the page layouts of my atomic design application. It’s not the richest folder of the app, but taking into consideration what the layouts are for, they are better isolated.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ui&lt;/code&gt; — Where the atoms and molecules of my application are. This is one of the heaviest folders in the application, so it is split up by domain subfolders.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pages&lt;/code&gt; — This corresponds to the pages defined in my application. This is the most complex folder because it is recursive. We’ll talk about it in a specific chapter right after this one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a lot of folders, right? The most difficult part of my perfect folder structure was to keep it simple (&lt;a href="https://en.wikipedia.org/wiki/KISS_principle"&gt;KISS!&lt;/a&gt;), but without mixing apples and oranges. This is why I placed atoms and molecules of atomic design in the same folder, but I also often have domain subfolders.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pages Subfolder
&lt;/h3&gt;

&lt;p&gt;Before coming to the folder structure, let’s talk about URLs. I found that cutting every URL in my app in two sections of the path (the domain and the page) is the simpler and more robust way to build the page path.&lt;/p&gt;

&lt;p&gt;I might also have additional parameters to show a specific detail page. These ones are not limited in amount.&lt;/p&gt;

&lt;p&gt;For example, I have these pages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/user/login&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/user/account&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/todo/list&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/todo/details/123&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I do not have these ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/user&lt;/code&gt; will redirect to &lt;code&gt;/user/dashboard&lt;/code&gt;, for example.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; will probably also redirect to &lt;code&gt;/user/dashboard&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These URLs give you a hint on how structured the folders will be. Without surprise, we have a first folder that is the domain and a second one that is the page.&lt;/p&gt;

&lt;p&gt;As I mentioned earlier, the page folder is also recursive. Why? Simply because sometimes the content is not global to the app. A &lt;code&gt;useTodoList&lt;/code&gt; hook is only used in the &lt;code&gt;/todo/list&lt;/code&gt; page and the &lt;code&gt;TodoItem&lt;/code&gt; component also.&lt;/p&gt;

&lt;p&gt;So inside a page folder, you can also find a &lt;code&gt;components&lt;/code&gt; folder with every folder defined earlier but &lt;code&gt;pages&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting It All Together
&lt;/h3&gt;

&lt;p&gt;That was a lot of words to define the overall structure. But an example is often better than words, so here it is:&lt;/p&gt;

&lt;pre&gt;
src  
 |- App  
 | |- App.jsx  
 |- components  
 | |- hooks  
 | | |- useInterval.jsx  
 | |- modals  
 | | |- AddTodoModal.jsx  
 | |- organisms  
 | | |- PrivateRoute.jsx  
 | | |- forms  
 | | | |- TextInput.jsx  
 | |- pages  
 | | |- todo  
 | | | |- list  
 | | | | |- TodoList.jsx  
 | | | | |- components  
 | | | | | |- hooks  
 | | | | | | |- useTodoList.jsx  
 | | | | | |- organisms  
 | | | | | | |- TodoItem.jsx  
 | | |- user  
 | | | |- login  
 | | | | |- UserLogin.jsx  
 | |- providers  
 | | |- UserProvider.jsx  
 | | |- TodoProvider.jsx  
 | |- svg  
 | | |- check.svg  
 | |- templates  
 | | |- LoggedPage.jsx  
 | | |- LoginPage.jsx  
 | |- ui  
 | | |- alert  
 | | | |- Alert.jsx  
 | | | |- Alert.module.css  
 | | | |- Alert.stories.jsx  
 | | | |- Alert.test.js  
 | | |- button  
 | | | |- Button.jsx  
 | | | |- Button.module.css  
 | | | |- Button.stories.jsx  
 | | | |- Button.test.jsx  
 |- gql  
 | |- todo  
 | | |- TodoCreate.gql  
 | | |- TodoDelete.gql  
 |- libs  
 |- preload.js
&lt;/pre&gt;

&lt;p&gt;Even if the example is pretty simple, it contains everything to illustrate the previous explanations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Even if this folder structure for React is a work of many years on how to organize a project, it might not suit every need. However, as of today, it fits all my projects’ needs and makes me particularly efficient during my work.&lt;/p&gt;

&lt;p&gt;If you encounter some issues of your own, I would be glad to hear from you about how this proposal is bringing you trouble. But remember, the right folder structure is not necessarily mine but the one that fits your project. After all:&lt;/p&gt;

&lt;blockquote&gt;“Move files around until it feels right.”  —  &lt;a href="https://react-file-structure.surge.sh/"&gt;Dan Abramov&lt;/a&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to Debug a React Context API App</title>
      <dc:creator>Bruno Sabot</dc:creator>
      <pubDate>Tue, 17 Mar 2020 17:45:56 +0000</pubDate>
      <link>https://dev.to/brunosabot/how-to-debug-a-react-context-api-app-fed</link>
      <guid>https://dev.to/brunosabot/how-to-debug-a-react-context-api-app-fed</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@barnimages"&gt;Barn Images&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some time ago, I shared how &lt;a href="https://medium.com/better-programming/how-i-dropped-redux-for-the-context-api-7338d481e179"&gt;I dropped Redux for the Context API&lt;/a&gt; when I’m creating a React application. The post got some great feedback, but I also had some people saying that it’s pretty hard to debug compared to the &lt;a href="https://github.com/reduxjs/redux-devtools"&gt;Redux DevTools&lt;/a&gt; and asking me if there is an easy method to do it.&lt;/p&gt;

&lt;p&gt;The answer is yes. Actually, if there is something awesome about Redux, it’s the DevTools. The great part is we can link them easily with our Redux-free app — and with everything we like, really.&lt;/p&gt;

&lt;p&gt;Let’s now see how it works!&lt;/p&gt;

&lt;h3&gt;
  
  
  Redux DevTools API
&lt;/h3&gt;

&lt;p&gt;When we have Redux DevTools installed, the extension automatically injects a special object (&lt;code&gt;__REDUX_DEVTOOLS_EXTENSION__&lt;/code&gt;) in the window. A weird name for sure, but it prevents any conflicts with your existing code.&lt;/p&gt;

&lt;p&gt;And that’s where everything starts: This object gives us everything we need — &lt;code&gt;connect&lt;/code&gt; and &lt;code&gt;disconnect&lt;/code&gt; methods that link our code with the Redux DevTools.&lt;/p&gt;

&lt;p&gt;However, if you just try to run these functions, you will still see nothing in the DevTools because we first need to initiate the session.&lt;/p&gt;

&lt;p&gt;To do so, we take advantage of the object that returns the &lt;code&gt;connect&lt;/code&gt; method. It possesses an init method that launches the DevTools session.&lt;/p&gt;

&lt;p&gt;Basically, it looks like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Simple Redux DevTools connection.




&lt;p&gt;Even if this works, you will still see nothing in the DevTools because the session is closed as soon as we create it.&lt;/p&gt;

&lt;h3&gt;
  
  
  A DevTools Provider
&lt;/h3&gt;

&lt;p&gt;To make the session permanent when you are developing your app, you will need a &lt;code&gt;Provider&lt;/code&gt;, which looks like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
A basic Redux DevTools Provider.




&lt;p&gt;If you add this piece of code at the very top of your application, you will see the startup session in the Redux DevTools that you can identify with the &lt;code&gt;@@INIT&lt;/code&gt; event that pops in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zwTHSJGB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://storage.googleapis.com/brunosabot.dev/img/1__1__Ee__Cxvnmx1bAgstmX3jA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zwTHSJGB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://storage.googleapis.com/brunosabot.dev/img/1__1__Ee__Cxvnmx1bAgstmX3jA.png" alt="The INIT event that pops in." width="800" height="343"&gt;&lt;/a&gt;&lt;br&gt;The INIT event that pops in.
  &lt;/p&gt;

&lt;h3&gt;
  
  
  Sending Events to the DevTools
&lt;/h3&gt;

&lt;p&gt;Now that we are able to start a session, the next step is to send an event to the DevTools, which is as simple as we can imagine: The &lt;code&gt;devTools&lt;/code&gt; object that we have created also provides a &lt;code&gt;send&lt;/code&gt; method, which takes a name and some data to illustrate the change.&lt;/p&gt;

&lt;p&gt;Basically, it looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Simple Redux DevTools connection with a sent event.




&lt;h3&gt;
  
  
  Putting Everything Together
&lt;/h3&gt;

&lt;p&gt;We now have everything to make it work. We will just add a few things in our &lt;code&gt;Provider&lt;/code&gt; to make it truly usable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A hook to allow simple event sending to the DevTools session.&lt;/li&gt;
&lt;li&gt;A Context API so the DevTools can be available in the whole project.&lt;/li&gt;
&lt;li&gt;A simple sending method with default name values.&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The usage is now pretty simple in the application. We need to call the &lt;code&gt;useReduxDevtools&lt;/code&gt; hook we created in the &lt;code&gt;Provider&lt;/code&gt; file to wrap the initial method into a high-order function that handles the debug session:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
Look how useReduxDevtools is called on line 10.




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--77YOUN-w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://storage.googleapis.com/brunosabot.dev/img/1__ZRD7PZ36M__dTOFaRBU6aRQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--77YOUN-w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://storage.googleapis.com/brunosabot.dev/img/1__ZRD7PZ36M__dTOFaRBU6aRQ.png" alt="And voilà, our Context API inside Redux DevTools!" width="800" height="341"&gt;&lt;/a&gt;&lt;br&gt;And voilà, our Context API inside Redux DevTools!
  &lt;/p&gt;

&lt;p&gt;Now when we are calling the &lt;code&gt;setTheme&lt;/code&gt; method, it pops in the DevTools so you can inspect what is going on!&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;It is very easy to connect any app to the Redux DevTools, and you can start debugging any app without Redux right now.&lt;/p&gt;

&lt;p&gt;Also, let’s revisit something I said in my previous post about the Context API: It’s an easy API to use and can replace Redux in many cases, but keep in mind that Redux is much larger than the Context API. Choose wisely if you need to make the switch.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>react</category>
      <category>redux</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
