<?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: Lazar Nikolov</title>
    <description>The latest articles on DEV Community by Lazar Nikolov (@nikolovlazar).</description>
    <link>https://dev.to/nikolovlazar</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%2F42445%2Fcbe6d18e-1dbb-418f-8bc6-fce523df610b.jpg</url>
      <title>DEV Community: Lazar Nikolov</title>
      <link>https://dev.to/nikolovlazar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nikolovlazar"/>
    <language>en</language>
    <item>
      <title>Low effort image optimization tips</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Tue, 26 Mar 2024 19:12:22 +0000</pubDate>
      <link>https://dev.to/nikolovlazar/low-effort-image-optimization-tips-gbj</link>
      <guid>https://dev.to/nikolovlazar/low-effort-image-optimization-tips-gbj</guid>
      <description>&lt;p&gt;“A picture is worth a thousand words”. So if a picture takes more than 4 seconds to load, does it mean that your website’s content fails to communicate a thousand words? In this blog post, we’ll learn how to identify unoptimized images, how to fix them, and how to validate the fix — so your website can speak volumes with highly-optimized images.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;I wish loading images was as simple as &lt;code&gt;&amp;lt;img src=”...”&amp;gt;&lt;/code&gt;, but it ain’t. Loading images is a tricky thing, and it involves more than an &lt;code&gt;img&lt;/code&gt; tag with a source attribute. Loading images in an unoptimized way can really hurt your page’s performance. See for yourself (screenshots are from &lt;a href="https://sentry.io/welcome" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flttfju5yx8wqfk51ou5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flttfju5yx8wqfk51ou5x.png" alt="A trace view indicating bad performances due to unoptimized images" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of those images take &lt;em&gt;a lot&lt;/em&gt; of time to load. Let’s see what actually we’re loading on that page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u87mg6sdbxeyhc4ztuy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6u87mg6sdbxeyhc4ztuy.jpg" alt="Large images showing up in Resources page in Sentry" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what a plain &lt;code&gt;&amp;lt;img src=””&amp;gt;&lt;/code&gt; page looks like under the hood. Here are all the red flags we can see from the first screenshot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The images are JPEGs (not the most website-friendly format nowadays)&lt;/li&gt;
&lt;li&gt;They’re immediately loaded (not letting the browser display text content first)&lt;/li&gt;
&lt;li&gt;They’re too heavy, so they take quite some time to load&lt;/li&gt;
&lt;li&gt;The page took about 6 seconds to load&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These three things caused the LCP to jump to ~4.2 seconds, the FP/FCP to 813ms, and also put too much work on the browser, which we can see from the many “Main UI thread blocked” spans (profiling revealed a connection between decoding images and long-running tasks on the main thread). Final verdict - &lt;strong&gt;the website takes too long to load and it freezes up&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s now see how we can use image optimization to improve performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ditch JPEG for AVIF + WebP
&lt;/h2&gt;

&lt;p&gt;As mentioned, JPEG is not the most website-friendly format nowadays. It produces larger files, doesn’t support transparency, and has visible blocky artifacts when exported with a lower quality. It’s not a modern image format.&lt;/p&gt;

&lt;p&gt;Enter AVIF and WebP. Both AVIF and WebP are modern image formats. Compared to JPEG, they both produce smaller file sizes, have less visible blocky artifacts, and support transparency. Additionally, AVIF has support for higher color depth, so color accuracy is better. WebP on the other hand has much better browser support. At the time of writing this article, &lt;a href="https://caniuse.com/webp" rel="noopener noreferrer"&gt;only Internet Explorer does not have support for WebP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How you can get to these formats varies by usage. If your images are static, you can use any of the image manipulation apps to re-export the JPEGs you might already have as AVIFs and WebPs. There are also web apps you can use to save yourself some storage space.&lt;/p&gt;

&lt;p&gt;If your images are dynamic, or your website has a lot of images, then you have a few options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate images at build time
&lt;/h3&gt;

&lt;p&gt;This approach is useful if you have all the images upfront and you would like to convert all of them before deploying your website. Bear in mind that it won’t work for dynamic images, since you won’t have them at build-time. You can use a Node library like &lt;a href="https://sharp.pixelplumbing.com/" rel="noopener noreferrer"&gt;sharp&lt;/a&gt; to achieve this. Write a JS script that reads all the images in your project, converts them to AVIF and WebP, and puts them in a different folder in your project. (Tweet me at &lt;a href="https://twitter.com/NikolovLazar" rel="noopener noreferrer"&gt;@NikolovLazar&lt;/a&gt; if you’re interested in seeing a more in-depth guide on this).&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate images on demand
&lt;/h3&gt;

&lt;p&gt;In a similar fashion as build time, but instead move the logic into an API endpoint and perform the conversion on demand. This could be an endpoint that accepts the static image name/path (&lt;code&gt;/assets/lazar.jpg&lt;/code&gt;), converts it on request and returns back the correct format. This approach allows you to generate only an AVIF image if the requesting client supports it, otherwise you can default to WebP. &lt;strong&gt;Make sure to cache the results!&lt;/strong&gt; If you have the time, you could also upload the generated images to a CDN and serve those from that point on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a third-party service
&lt;/h3&gt;

&lt;p&gt;Of course, if you don’t want to do all of this work above, you can opt-in and pay for a third-party service, like, &lt;a href="https://www.imgix.com/" rel="noopener noreferrer"&gt;imgix&lt;/a&gt;, &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt;, &lt;a href="https://vercel.com/docs/image-optimization" rel="noopener noreferrer"&gt;Vercel’s Image Optimization&lt;/a&gt; (if you’re using Next.js), or others. Using these services in your app is a matter of implementing their SDKs and using their components instead of plain &lt;code&gt;img&lt;/code&gt; tags&lt;/p&gt;

&lt;h2&gt;
  
  
  Properly size images
&lt;/h2&gt;

&lt;p&gt;Those “Main UI thread blocked” spans are nasty. Those are moments when the website is frozen. They happen because the intrinsic size of the images is a lot bigger than their rendered size, so the browser needs to spend time rescaling them on the fly. There are a couple of ways to go about this, and it involves tapping into the previous section’s mechanism.&lt;/p&gt;

&lt;p&gt;If you &lt;strong&gt;know the rendered dimensions&lt;/strong&gt; of the image, you can modify your generation mechanism (or image request) to scale up/down the image to the right dimensions.&lt;/p&gt;

&lt;p&gt;If you &lt;strong&gt;don’t know the rendered dimensions&lt;/strong&gt;, the easiest way to go about this is to simply make sure your images’ dimensions are within the ballpark of what’s possible. Let’s say your layout doesn’t span more than 768px horizontally, and that the images can be full width. The least you can do is to make sure that the images aren’t more than 768px wide. If the majority of your website’s traffic comes from mobile phones, figure out the largest width in that category and generate another set of images for it.&lt;/p&gt;

&lt;p&gt;Either way, you’ll see improvements over using original images that are too large for your website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use a &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element instead of just &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In the previous sections we mentioned generating multiple image formats and multiple image resolutions, but how do you put all of that together? Out with the &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, in with the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element allows us to specify “image resources”. It still renders a single image, but it allows us to specify all the formats and resolutions we support, and leaves it to the browser to pick the best option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 1024px)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/optimized/large.avif"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 350px)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/optimized/small.avif"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A photo"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/optimized/fallback.webp"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how you can define a picture element that renders two AVIF images based on the viewport’s width, but also provides a fallback WebP image in case the browser does not support the AVIF format. This ensures that the browser will download the smallest possible image, which will improve the time to fetch the image and the time to render it.&lt;/p&gt;

&lt;p&gt;If you opted-in to use a third-party service, look at its documentation to see if the platform itself supports automatic format/resolution, and if not, how you can specify the format and resolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose lazy loading
&lt;/h2&gt;

&lt;p&gt;When you’re loading your images directly impacts the &lt;a href="https://docs.sentry.io/product/performance/web-vitals/#largest-contentful-paint-lcp" rel="noopener noreferrer"&gt;LCP core web vital&lt;/a&gt;. You can change the way you’re loading your images through the &lt;code&gt;loading&lt;/code&gt; attribute. There is no silver bullet solution, so when you should render your images &lt;strong&gt;will depend&lt;/strong&gt; on a few factors.&lt;/p&gt;

&lt;p&gt;If you &lt;strong&gt;know the LCP element&lt;/strong&gt; - for example, a static image that you add to the page manually - you’re better off with &lt;code&gt;loading=”eager”&lt;/code&gt; (which is the default btw). This tells the browser to start loading the image immediately, even before the First Paint event. The earlier the browser starts to load the image, the earlier it will finish loading, and the earlier the LCP event happens.&lt;/p&gt;

&lt;p&gt;If you &lt;strong&gt;don’t know the LCP element&lt;/strong&gt; (dynamic content), or for any images below the fold, you’re better off with &lt;code&gt;loading=”async”&lt;/code&gt;. This tells the browser to defer loading the image until it’s in the viewport. For images below the fold, the LCP score won’t be affected since they don’t count, but also the browser won’t spend any time downloading them. For above the fold images that you can’t predict (dynamic content), you’re still better off loading them lazily. Here’s why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamic content can cause multiple images to be loaded above the fold&lt;/li&gt;
&lt;li&gt;Dynamic content images can also mean different sizes and aspect ratios, which makes the LCP element inconsistent too&lt;/li&gt;
&lt;li&gt;Loading all of those images eagerly will slow down the page load, hurting your LCP score&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we were to add lazy loading to the &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element above, it would be like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 1024px)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/optimized/large.avif"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 350px)"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"/optimized/small.avif"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"A photo"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/optimized/fallback.webp"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;                                          ^ set it on the img element
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The end result
&lt;/h2&gt;

&lt;p&gt;So, if you apply all of the image optimizations above, you get a totally different picture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckjoxsl3vuigyzy24cv1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fckjoxsl3vuigyzy24cv1.png" alt="A trace view showing a significantly improved performances after optimizing the images" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Night and day! Here’s what you can see from the screenshot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Images load significantly faster (AVIF + scaled down + lazy loaded)&lt;/li&gt;
&lt;li&gt;LCP brought down to 363ms, while FP/FCP to 98ms&lt;/li&gt;
&lt;li&gt;Significantly fewer UI thread blocks&lt;/li&gt;
&lt;li&gt;The whole page load finished in 873ms&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Converting the images to AVIF and downscaling them also means that the whole page will take a lot less bandwidth to load, which also means your page will be “cheaper” to load. If you’re a mobile user living in a country where data plans are expensive, this can be especially helpful.&lt;/p&gt;

&lt;p&gt;So there you go. Four easy image optimization tips that can really make a difference in your website’s performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;Performance: Web Vitals | Technical Demo - &lt;a href="https://www.youtube.com/watch?v=M0ROqRw2Fgs" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=M0ROqRw2Fgs&lt;/a&gt;&lt;br&gt;
Performance: Web Vitals | Docs - &lt;a href="https://www.youtube.com/watch?v=M0ROqRw2Fgs" rel="noopener noreferrer"&gt;https://docs.sentry.io/product/performance/web-vitals/&lt;/a&gt;&lt;br&gt;
Performance: Resources | Docs - &lt;a href="https://www.youtube.com/watch?v=M0ROqRw2Fgs" rel="noopener noreferrer"&gt;https://docs.sentry.io/product/performance/resources/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>Fixing Fetch Waterfalls in React</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Fri, 22 Dec 2023 19:54:19 +0000</pubDate>
      <link>https://dev.to/nikolovlazar/fixing-fetch-waterfalls-in-react-58g5</link>
      <guid>https://dev.to/nikolovlazar/fixing-fetch-waterfalls-in-react-58g5</guid>
      <description>&lt;p&gt;Have you seen this problem?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs103cd3f271klrblawa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs103cd3f271klrblawa.png" alt="Fetch Waterfall in Sentry" width="800" height="100"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or maybe this one?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80tad1xz1mqwtivinjge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80tad1xz1mqwtivinjge.png" alt="Fetch Waterfall in Chrome" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ve most likely seen this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;api&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;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Spinner&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AnotherComponent&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;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Hint: they’re all the same. The first image is &lt;a href="https://docs.sentry.io/product/sentry-basics/concepts/tracing/event-detail" rel="noopener noreferrer"&gt;Sentry’s Event Details page&lt;/a&gt;, the second is &lt;a href="https://developer.chrome.com/docs/devtools/network/" rel="noopener noreferrer"&gt;Chrome’s Network tab&lt;/a&gt;, and the code snippet is what causes it.&lt;/p&gt;




&lt;p&gt;If you can answer yes to any of these, then you need to keep reading. If not, you still need to keep reading, so your future self can thank you.&lt;/p&gt;

&lt;p&gt;This is called &lt;strong&gt;“fetch waterfall”&lt;/strong&gt; and it’s a common data fetching issue in React. It happens when you create a hierarchy of components that fetch their own data and show a “Loading” state before rendering their child component, which then does the same etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A fetches its data and then renders B
B fetches its data and then renders C
C fetches its data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would be fine if each component’s data depends on its parent’s, but that’s not always the case. If each fetch takes at least a second, then you’re looking at &lt;strong&gt;3+ seconds of page load&lt;/strong&gt;, which is considered &lt;strong&gt;slow&lt;/strong&gt; by today’s standards. But why wait for 3+ seconds for data that you can get in 1 second if it’s fetched &lt;strong&gt;in parallel&lt;/strong&gt;? &lt;a href="https://codesandbox.io/p/sandbox/fetch-waterfall-tmqw6d" rel="noopener noreferrer"&gt;Here’s a CodeSandbox of this issue&lt;/a&gt; so you can see for yourself.&lt;/p&gt;

&lt;p&gt;Let’s explore three ways to maintain good performance and scenarios when you would want to use each solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using “Suspense” to avoid a fetch waterfall
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://react.dev/reference/react/Suspense" rel="noopener noreferrer"&gt;Suspense&lt;/a&gt; is oftentimes mentioned as one possible solution to avoid fetch waterfall. And, yes, it does solve the issue. It triggers the whole component tree’s fetches in parallel, so the data gets fetched much faster. But, it’s still &lt;strong&gt;not production ready&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;React’s docsite explains that only Suspense-enabled data sources will activate the Suspense component, and those are frameworks (like &lt;a href="https://relay.dev/docs/guided-tour/rendering/loading-states/" rel="noopener noreferrer"&gt;Relay&lt;/a&gt; and &lt;a href="https://nextjs.org/docs/getting-started/react-essentials" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;), or lazy-loading components with &lt;a href="https://react.dev/reference/react/lazy" rel="noopener noreferrer"&gt;&lt;code&gt;lazy&lt;/code&gt;&lt;/a&gt;, or reading the promise value with &lt;a href="https://react.dev/reference/react/use" rel="noopener noreferrer"&gt;&lt;code&gt;use&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fira0edepe3auotkvrr1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fira0edepe3auotkvrr1k.png" alt="Waterfall fixed with Suspense" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lazy-loading and the &lt;code&gt;use&lt;/code&gt; hook are still not necessarily good solutions. Lazy-loading components won’t eliminate the fetch waterfall. It’ll behave the same way, with just the “loading” fallback from the suspense as the bonus. The &lt;code&gt;use&lt;/code&gt; hook is only available in React’s canary version, so it hasn’t been released as stable just yet.&lt;/p&gt;

&lt;p&gt;So, if unless you’re using either Next.js or Relay, I wouldn’t advise you to use Suspense as a solution just yet.&lt;/p&gt;

&lt;p&gt;But, if you &lt;em&gt;really&lt;/em&gt; want to, check out &lt;a href="https://codesandbox.io/p/sandbox/fetch-waterfall-suspense-fix-gwqmqw" rel="noopener noreferrer"&gt;this CodeSandbox example&lt;/a&gt;. When it is production-ready it could be a great way to maintain good performance and avoid the fetch waterfall.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fetch data on server to avoid a fetch waterfall
&lt;/h2&gt;

&lt;p&gt;I hear you, you’re saying the solution is obvious, just make that page server-side rendered, or if you use Next.js - use &lt;a href="https://nextjs.org/docs/app/building-your-application/rendering/server-components" rel="noopener noreferrer"&gt;Server Components&lt;/a&gt;. Then the client receives the HTML along with the data and doesn’t have to make any data requests. No requests = no fetch waterfall!&lt;/p&gt;

&lt;p&gt;Okay, but then would you fetch the data on the server-side like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pokemon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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;encounters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&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;locations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for &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;encounter&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;encounters&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;location&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 that was your idea, you’re just passing the ball to the server. The server will still wait to fetch all of the data in series before serving everything back to the client. This is a concurrency problem, not a rendering problem. Just fetching the data on the server won’t automatically solve it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13qpqvalil5hak8jchn0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13qpqvalil5hak8jchn0.png" alt="Fetch Waterfall on Server Components" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yeah, that definitely looks like a waterfall 😁&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In fact, you actually made it worse. On a classic SSR (or Server Components without a &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming" rel="noopener noreferrer"&gt;“loading”&lt;/a&gt; component) the TTFB is now slower than the time it takes to fetch the data. And since &lt;a href="https://docs.sentry.io/product/performance/web-vitals/#time-to-first-byte-ttfb" rel="noopener noreferrer"&gt;TTFB&lt;/a&gt; has a larger value, all the other web vitals will too!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpbq905tmhtcuduub04ev.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpbq905tmhtcuduub04ev.png" alt="Web Vitals Difference" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You could fix the TTFB in Next.js by also providing a loading component, but the waterfall will still keep on falling.&lt;/p&gt;

&lt;p&gt;Another thing to consider is that when doing SSR/Server Components, you still render on the browser. The client still needs to display the DOM it got from the server, and then hydrate it so it can be able to “react 😉” to the user interactions. And now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You have two computers (the browser and your server) performing rendering tasks.&lt;/li&gt;
&lt;li&gt;The server has a lot more work to do on each request.&lt;/li&gt;
&lt;li&gt;The browser is still showing a blank page to your users (when SSR), and still does some rendering when it gets the response back.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;encounters&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nf"&gt;fetchPokemon&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="nf"&gt;fetchEncounters&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;encounters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;encounter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three fetch methods can be anything - other API requests or database calls - but must return a Promise. &lt;code&gt;Promise.all&lt;/code&gt; will trigger all input promises in parallel, and will return a new promise that gets resolved when the last of the input promises gets resolved.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3urt9kzybr0np2rvb5db.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3urt9kzybr0np2rvb5db.png" alt="Fetch Waterfall page load fixed" width="800" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hoisting the data fetching to avoid a fetch waterfall
&lt;/h2&gt;

&lt;p&gt;Another solution to the fetch waterfall issue is hoisting the data fetching to an upper level in the component hierarchy. Instead of fetching the data at the component level, you can fetch it at the topmost level in your component tree (the first component that starts to fetch the data), and pass it down to the component tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App
└── DataContext.Provider
    ├── PokemonPage (useContext -&amp;gt; pokemon)
    └── PokemonEncounters (useContext -&amp;gt; encounters)
        ├── LocationDetails (useContext -&amp;gt; otherPokemon)
        ├── LocationDetails (useContext -&amp;gt; otherPokemon)
        ├── LocationDetails (useContext -&amp;gt; otherPokemon)
        └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fetching the data in parallel is still important in this case, so make sure you do that.&lt;/p&gt;

&lt;p&gt;So - &lt;code&gt;fetch&lt;/code&gt; it when the first component mounts, and then pass the data down. Does it solve the problem? Yes. Does it couple the components and introduce prop drilling? Double yes. You can solve that with a Context Provider, so all of the children components can access the part of the data they’re interested in without prop drilling.&lt;/p&gt;

&lt;p&gt;I’ve prepared &lt;a href="https://codesandbox.io/p/sandbox/fetch-waterfall-hoist-context-fix-nqysjd" rel="noopener noreferrer"&gt;a CodeSandbox example&lt;/a&gt; for you, if you want to see how you can implement a hoisted data fetching with a React Context. Here’s how the our requests look like now:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i75sqp3937kryfvcs3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i75sqp3937kryfvcs3p.png" alt="Fetch Waterfall fixed with context" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/pikachu&lt;/code&gt; and &lt;code&gt;/pikachu/encounters&lt;/code&gt; requests start at the same time, while the &lt;code&gt;/location-area&lt;/code&gt; requests depend on the result of the &lt;code&gt;/pikachu/encounters&lt;/code&gt; request, but they too start at the same time. We broke out the waterfall.&lt;/p&gt;

&lt;p&gt;Hoisting the data fetching mechanism is probably the most universal approach you can take to eliminate the fetch waterfall. It happens on the client side, so your server does less work and your app’s web vitals are lower. It doesn’t rely on experimental features like Suspense, and it doesn’t work only in Next.js, or when using Relay.&lt;/p&gt;

&lt;p&gt;Whether you fetch it in a React Context, or fetch it in the parent component and pass it down through the props, it’s up to you. It depends on how your component structure looks like, what you and your team are comfortable with, and what other libraries you use in your project.&lt;/p&gt;

&lt;p&gt;For example, if you use &lt;a href="https://tanstack.com/query/latest/docs/react/overview" rel="noopener noreferrer"&gt;TanStack Query&lt;/a&gt;, you can perform all the fetches and cache the results in a higher level of the component hierarchy, but then use the &lt;a href="https://tanstack.com/query/latest/docs/react/reference/QueryClient" rel="noopener noreferrer"&gt;QueryClient&lt;/a&gt; in each of the components to read the data from the cache. It’s still considered as hoisting, and it can be an alternative if you don’t want to create a new Context. Simply make the fetches where you would put the Context Provider, and read the data from the cache instead of the provider in the components.&lt;/p&gt;

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

&lt;p&gt;So what we can learn from this article is this - there are two things that you can do to cause a fetch waterfall:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Nest components that fetch their own data while conditionally rendering them.&lt;/li&gt;
&lt;li&gt;Invoke the requests sequentially (await first, then await second, then await third and so on).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re fetching the data on the client side, make sure you’re not fetching the data in each component while also conditionally rendering them. If each component fetches its own data, and you display them sequentially, then they’re going to sequentially trigger their data fetches -&amp;gt; waterfall.&lt;/p&gt;

&lt;p&gt;Hoisting the data fetching mechanism to a more upper-level component is a good fix. It depends on your structure, but you can either fetch it in the parent component and pass it down through the props, create a React Context if passing down the data causes prop drilling, or if you already use a library like TanStack Query - perform the fetches at a higher level and use the &lt;code&gt;QueryClient&lt;/code&gt; to read the results from the cache.&lt;/p&gt;

&lt;p&gt;Just moving the data fetching to the server side doesn’t mean that you’ll fix the problem. If you’re &lt;code&gt;await&lt;/code&gt;-ing each fetch request individually, you’ll still cause a fetch waterfall, but this time on the server-side. This way the TTFB web vital also suffers because the client needs to wait for the server to do all the data fetching before it starts to draw pixels. So not only you didn’t fix the problem, you introduced a new one.&lt;/p&gt;

&lt;p&gt;Being mindful of how you build your app, and keeping an eye on its performance in production is how you win in this game.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>webdev</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>What’s the difference between API Latency and API Response Time?</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Mon, 13 Nov 2023 15:28:32 +0000</pubDate>
      <link>https://dev.to/sentry/whats-the-difference-between-api-latency-and-api-response-time-1198</link>
      <guid>https://dev.to/sentry/whats-the-difference-between-api-latency-and-api-response-time-1198</guid>
      <description>&lt;p&gt;Your app’s networking directly affects the user experience of your app. Imagine having to wait a few seconds for the page to load. Or even worse, imagine waiting for a few seconds every time you perform an action. It would be infuriating! Before you go on a fixing adventure, it’s a good idea to understand what causes that waiting time. So let’s do that!&lt;/p&gt;

&lt;h2&gt;
  
  
  The difference between Latency vs Response Time
&lt;/h2&gt;

&lt;p&gt;The total client-server communication time is called &lt;strong&gt;API Response Time&lt;/strong&gt;. That’s from the moment the client makes a request, until it receives a response back. A slow request means a very long response time. Anything that contributes to the slowness affects the response time.&lt;br&gt;
The response time consists of the &lt;strong&gt;latency&lt;/strong&gt; and the &lt;strong&gt;processing time&lt;/strong&gt;. The latency is how long it takes to transfer data between the client and the server, or the “processing component”. A few factors contribute to the latency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the network speed&lt;/li&gt;
&lt;li&gt;the server load&lt;/li&gt;
&lt;li&gt;the performance of the load balancer&lt;/li&gt;
&lt;li&gt;the size of the data we’re sending&lt;/li&gt;
&lt;li&gt;and the API design&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/em6l9zw4tzag/3HSe1PeCYHhJ3XzXY4Waka/c1512a223d3988396c4718a84dccdc03/response-time-latency.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/em6l9zw4tzag/3HSe1PeCYHhJ3XzXY4Waka/c1512a223d3988396c4718a84dccdc03/response-time-latency.png" alt="API Response Time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, the client sends a data request to the API and gets a response back in 3 seconds. The total response time is 3 seconds. The latency might be 300ms, which leaves 2500ms, or 2.5 seconds, for processing. Just to have a number in mind, high-performing APIs are considered to have between 0.1 and 1 second average response time. At 2 seconds the delay is noticeable. At 5 seconds the delay is so significant that the users are starting to abandon the application/website.&lt;/p&gt;

&lt;p&gt;The response time is one of the most important metrics we need to keep our eyes on. If not, it can significantly hurt the user experience, and even the business itself. Back in 2008, &lt;a href="https://news.ycombinator.com/item?id=273900" rel="noopener noreferrer"&gt;Amazon reported&lt;/a&gt; that every 100ms latency costs 1% of their profit. &lt;a href="http://glinden.blogspot.com/2006/11/marissa-mayer-at-web-20.html" rel="noopener noreferrer"&gt;Google also reported&lt;/a&gt; that in an experiment that increased the number of search results they noticed that half a second delay caused a 20% drop in traffic. These are significant numbers. Amazon makes $19.4 billion per month. 1% of those sales is $194 million. Add up that number for every 100ms latency and you’ll see the damage. As you can see, learning how to monitor and optimize your API response time is a very good investment.&lt;/p&gt;

&lt;p&gt;Just like any other web performance metric, the response time should be measured and monitored in production. Your computer and internet speed might be fast, but your users’ computers and internet are probably not as fast. That results in much less favorable results than what you’ll see if you measured your API’s response time locally. But working with production data also means that you’re working with outliers. The Average Response Time is not always a good metric. Let’s learn how to properly measure the API response time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Measuring response time
&lt;/h2&gt;

&lt;p&gt;First let’s talk about why ART (Average Response Time) is not a good metric. There are always the edge cases where a small number of your users are reporting the worst response times imaginable. That could be because of a really outdated computer, or they’re trying to access your web app with a bad internet connection (subway, remote locations, etc…), or your API experienced a brief downtime because of a bug or your deployment. You don’t care about those cases because there is usually nothing you can do about them. Calculating an average on that data will take those outliers into account as well, and you don’t want that. You want to exclude those data points from your data. Enter: &lt;strong&gt;percentiles&lt;/strong&gt;.&lt;br&gt;
Percentiles provide you with a different view of your performance data. The data is sorted in a descending order and cut at specific % points. The most commonly used percentiles are p50, p75, p95, p99 and p100.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;P50, also called the Median&lt;/strong&gt;, is the value below which 50% of the data falls. This would be the typical performance of your API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P75&lt;/strong&gt; is the value where 75% of the data falls. This percentile is good for frontend applications because it includes more variable data, which mirrors the variety of the user conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P95&lt;/strong&gt; is more valuable for backend applications where the data is more uniform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P99, also called Peak Response Time&lt;/strong&gt;, is also more valuable for backend applications, and it marks the upper limit of the performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;P100&lt;/strong&gt; is the maximum observed value. This is the worst measured performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to get into more detail about percentiles, read our &lt;a href="https://blog.sentry.io/choosing-the-right-metric-a-guide-to-percentiles-perf-monitoring/#percentiles-understanding-variability" rel="noopener noreferrer"&gt;“Choosing the Right Metric” article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another important metric is the Error/Failure Rate of your API. The Error/Failure Rate is a value that indicates the % of requests that resulted with a non-200 status code. Having this data can also help you figure out if things are wrong with your API or documentation. If you’re seeing more “400s” status codes, it might mean that clients don’t use your API properly, or don’t understand it well. If you’re seeing more “500s” status codes then it means you have issues with your code.&lt;/p&gt;
&lt;h2&gt;
  
  
  Monitoring API response time in production
&lt;/h2&gt;

&lt;p&gt;To monitor your API’s response time (and other metrics as well) in production, you need to implement a mechanism that will continuously take those measurements, save them in a database, and provide you with tools to query and visualize the data. Sentry is an &lt;a href="https://sentry.io/for/performance/" rel="noopener noreferrer"&gt;application performance monitoring tool&lt;/a&gt; that you can use to monitor all of your &lt;a href="https://sentry.io/for/frontend/" rel="noopener noreferrer"&gt;frontend&lt;/a&gt;, &lt;a href="https://sentry.io/for/backend/" rel="noopener noreferrer"&gt;backend&lt;/a&gt;, and &lt;a href="https://sentry.io/for/mobile/" rel="noopener noreferrer"&gt;mobile application&lt;/a&gt; projects—not just your API. And don’t worry, Sentry probably has an &lt;a href="https://sentry.io/platforms/" rel="noopener noreferrer"&gt;SDK&lt;/a&gt; for whatever framework you’re using.&lt;/p&gt;

&lt;p&gt;Getting started is easy. After installing and configuring Sentry’s SDK for your framework of choice, you’ll get automatic instrumentation, which means a bigger part of your backend is already set up with performance monitoring. Depending on the size of your application, this could be enough to get started. If you need more detailed performance data, you can add &lt;a href="https://docs.sentry.io/platforms/node/performance/instrumentation/custom-instrumentation/" rel="noopener noreferrer"&gt;custom instrumentations&lt;/a&gt; in certain places.&lt;/p&gt;

&lt;p&gt;When you start getting performance data in your Sentry account, identifying what makes your API slow is quick. Getting to the root cause of the slow performance is even quicker. Sentry’s tracing data makes the performance bottlenecks obvious. Here’s a short arcade that shows you how you can identify performance bottlenecks in your application and figure out what causes them:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://app.arcade.software/share/DsIzAmoLxA4bW8nqsiOZ" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.arcade.software%2Fog%2FDsIzAmoLxA4bW8nqsiOZ" height="630" class="m-0" width="1200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://app.arcade.software/share/DsIzAmoLxA4bW8nqsiOZ" rel="noopener noreferrer" class="c-link"&gt;
            Backend Performance Monitoring | Arcade
          &lt;/a&gt;
        &lt;/h2&gt;
          
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.arcade.software%2Fimages%2Fbutton-logo-128.png" width="128" height="128"&gt;
          app.arcade.software
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;A good resource on improving the performance of your API using Sentry is Lincoln Loop’s “&lt;a href="https://lincolnloop.com/insights/optimizing-response-time-19x-faster/" rel="noopener noreferrer"&gt;19x faster response time&lt;/a&gt;” article.&lt;/p&gt;

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

&lt;p&gt;So, a quick recap. API latency is the time it takes for the data to be transmitted between the client and the backend. The API response time is the latency + the time it takes for the backend to process the request and return the result. Factors like network speed, load balancer, data size, server load, API design, and our code affect the response time.&lt;br&gt;
To properly monitor the performance of your backend, you need to use a tool that will monitor your application while in production. Sentry can help you with that, head to &lt;a href="https://sentry.io/signup" rel="noopener noreferrer"&gt;https://sentry.io/signup&lt;/a&gt; to get started.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>performance</category>
    </item>
    <item>
      <title>CSS Style Queries</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Thu, 31 Aug 2023 02:02:23 +0000</pubDate>
      <link>https://dev.to/creatures-dev/css-style-queries-5i</link>
      <guid>https://dev.to/creatures-dev/css-style-queries-5i</guid>
      <description>&lt;p&gt;Style Queries is a very cool feature in CSS, and they’re defined by the containment spec. Aside from being able to query a parent’s inline size (that’s container queries), the containment spec also includes the ability to query a parent’s style values (that’s style queries). Even though the container queries are available in all modern browsers, the style queries aren’t. Chrome and Edge have it. Opera’s working on it. Firefox and Safari, not so much. If you haven’t learned about container queries so far, check out our &lt;a href="https://creatures.dev/blog/css-container-queries/" rel="noopener noreferrer"&gt;Container Queries&lt;/a&gt; article. Style queries are just slightly different than container queries, so I’d suggest to learn about container queries first. Alright, let’s play with some style queries!&lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax
&lt;/h2&gt;

&lt;p&gt;Here’s our first example:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/nikolovlazar/embed/NWeWxov?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The markup is simple. We have a list of collection of cards who have an image, a title and some text. We can see that the images have an overlay with different colors. We can call them different variants of the card, and it’s a really good example for demonstrating style queries. Let’s break down the example. In the markup, we can see that we’re defining a CSS variable on every &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; element: &lt;code&gt;style='--variant: teal'&lt;/code&gt;, and that’s all we need to do in our markup. Then in our CSS we define a container query, but instead of querying on its size, we’re querying on its specific style value, which is the variable we defined earlier: &lt;code&gt;@container style(--variant: teal)&lt;/code&gt;. This creates a containment context on the element that has the &lt;code&gt;--variant: teal&lt;/code&gt; variable, and that’s the first &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; element. Just like the container queries, everything we put inside has to target a descendant of that &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; element. In our case we’re targeting the &lt;code&gt;.poster&lt;/code&gt; element, which is the image, and we’re setting its &lt;code&gt;::after&lt;/code&gt; element’s content to a specific SVG image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;teal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.poster&lt;/span&gt;&lt;span class="nd"&gt;::after&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("data:image/svg+xml;base64,PHN2Zy.........)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now repeat the same for the other variants &lt;code&gt;cream&lt;/code&gt;, &lt;code&gt;mint&lt;/code&gt;, &lt;code&gt;pink&lt;/code&gt;, &lt;code&gt;yellow&lt;/code&gt; and &lt;code&gt;sky&lt;/code&gt;, and our card variants will be done!&lt;/p&gt;

&lt;p&gt;You might’ve also noticed that even though the style queries are built on top of container queries, we didn’t define the &lt;code&gt;container&lt;/code&gt; or &lt;code&gt;container-name&lt;/code&gt; anywhere. CSS is smart enough to figure it out for you. But what if you have multiple components that use the &lt;code&gt;--variant&lt;/code&gt; variable? In that case, you can explicitly define the container name, so your style queries don’t mix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="py"&gt;container-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;daily-mix-card&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="n"&gt;daily-mix-card&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;teal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Combining multiple styles
&lt;/h2&gt;

&lt;p&gt;Let’s see another example:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/nikolovlazar/embed/LYMYqep?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In this example we’re using the variables to indicate whether certain things should be visible in the card. There are three variables: &lt;code&gt;---star-seller&lt;/code&gt;, &lt;code&gt;---order-soon&lt;/code&gt;, and &lt;code&gt;---free-delivery&lt;/code&gt;. All of them when set to &lt;code&gt;true&lt;/code&gt; will make the “✪ Star Seller”, “Only X left - order soon” and “FREE delivery” (respectfully) elements visible. If you look at line 29 in the HTML you’ll see that we have two variables defined: &lt;code&gt;--star-seller&lt;/code&gt; and &lt;code&gt;--order-soon&lt;/code&gt;. And if you look at the “EWB Bench” item, you’ll see that both the purple “Star Seller” and the red “Only 1 left - order soon” labels are displayed. Or check out the vintage lamp item with the red border around its image. It has all three styles set to true. To define a style query that queries on multiple styles, use &lt;code&gt;and&lt;/code&gt; to combine the &lt;code&gt;style(...)&lt;/code&gt; functions like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--star-seller&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--free-delivery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--order-soon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;red&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;With this approach, we can create and combine as many styles as we like. The best thing about it is that everything is controlled just by the &lt;code&gt;style&lt;/code&gt; attribute of the &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; element. If we want to dynamically control that using JavaScript, all we need to do is invoke the &lt;code&gt;setProperty&lt;/code&gt; method of the element’s style property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--star-seller&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Just imagine what you can do with style queries. All that with just pure CSS! No JavaScript logic to show/hide or conditionally render certain elements. Style queries allow us to define variants of our components, or toggle the visibility of certain elements, all just by setting a single CSS variable at the root element. We can also dynamically add/remove/change the variables with JavaScript by using the &lt;code&gt;style.setProperty()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Another benefit of this feature is the separation of data from design. The HTML and CSS define all the possible varieties, while the JavaScript only toggles them. And since we’re talking about plain JavaScript, we can use style queries to define our variants and togglable elements without being constrained to a specific UI framework! That’s why this CSS feature is a game changer, and I can’t wait for it to be supported by all modern browsers.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>CSS Container Queries</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Thu, 31 Aug 2023 01:59:52 +0000</pubDate>
      <link>https://dev.to/creatures-dev/css-container-queries-1idj</link>
      <guid>https://dev.to/creatures-dev/css-container-queries-1idj</guid>
      <description>&lt;p&gt;Unlike Media Queries, which let you apply styles to an element based on the viewport size, Container Queries let you apply styles to an element based on its own size. Talk about next level responsive design! And the best thing is, it’s supported by all modern browsers! Let’s say you want a card’s layout to be horizontal if it has at least a certain width, and switch to vertical when it gets narrower. Here’s an example:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/nikolovlazar/embed/vYvYRWr?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Containment context
&lt;/h2&gt;

&lt;p&gt;Looking at the example, we can see that our &lt;code&gt;article.post-card&lt;/code&gt; has a &lt;code&gt;container&lt;/code&gt; property with a value of &lt;code&gt;post-card / inline-size&lt;/code&gt;. This declares a containment context on our post card element. The &lt;code&gt;container&lt;/code&gt; is a shorthand property that sets the &lt;code&gt;container-name&lt;/code&gt; to &lt;code&gt;post-card&lt;/code&gt; and the &lt;code&gt;container-type&lt;/code&gt; to &lt;code&gt;inline-size&lt;/code&gt;. The &lt;code&gt;inline-size&lt;/code&gt; value declares the containment context on the inline axis of the container. This means that you can only define styles based on the width of the container. If you also want to define styles based on the height, you can use the &lt;code&gt;size&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;Bear in mind that when you declare a container to a certain element, &lt;strong&gt;it will prevent it from being sized based on its contents&lt;/strong&gt;. This goes for both &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;inline-size&lt;/code&gt;. To provide size to the “containerized” element, you would need to either define it through its parent (flex and grid stretch by default) or its display (block also stretches by default), or set its &lt;code&gt;width&lt;/code&gt; or &lt;code&gt;height&lt;/code&gt; explicitly. Setting the &lt;code&gt;container-type&lt;/code&gt; to &lt;code&gt;size&lt;/code&gt; will collapse its height, while setting it to &lt;code&gt;inline-size&lt;/code&gt; will collapse its width.&lt;/p&gt;

&lt;p&gt;Another thing to have in mind is that you can’t use an inline element as a container. If you were to define a &lt;code&gt;span&lt;/code&gt; as a container, you could, but you’d have to make it a non-inline display. &lt;strong&gt;Rule of thumb: any element that’s not inline can be made into a container&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Container query
&lt;/h2&gt;

&lt;p&gt;Check out line 40. We set the &lt;code&gt;.content&lt;/code&gt; element’s &lt;code&gt;flex-direction&lt;/code&gt; to column. On smaller sizes we want the image and text to be one on top of the other, but if we have enough horizontal space we can actually put them one next to the other, or set the &lt;code&gt;flex-direction&lt;/code&gt; to row. That’s a use case for a container query! Scroll down to line 96:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@container&lt;/span&gt; &lt;span class="n"&gt;post-card&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;512px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how we define a container query. With this line, we’re basically telling CSS “when the post-card container (not element with class) has at least 768px of width, apply these styles (…)”. The top level scope is the &lt;code&gt;article.card&lt;/code&gt; element. We should take that in consideration when writing the selectors. On line 99 we redefine the &lt;code&gt;.content&lt;/code&gt; element’s &lt;code&gt;flex-direction&lt;/code&gt; to &lt;code&gt;row&lt;/code&gt;. That’ll make the image and text to flow horizontally, and if we expand the viewport enough we’ll see that the card’s content changes direction. Notice that if we change the selector to &lt;code&gt;article.post-card div.content&lt;/code&gt; it won’t work, even though it’s a perfectly valid selector, because it would expect to find an &lt;code&gt;article.post-card&lt;/code&gt; inside of the &lt;code&gt;article.post-card&lt;/code&gt; element and we both know that’s wrong. But following this example, we can redefine any property of any descendant of our &lt;code&gt;article.post-card&lt;/code&gt;. That’s the beauty of it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Container query units
&lt;/h2&gt;

&lt;p&gt;Along with the new &lt;code&gt;@container&lt;/code&gt; syntax, we also get brand new values that are relative to the container size. Here are they:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cqw&lt;/code&gt; is 1% of the container’s width&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cqh&lt;/code&gt; is 1% of the container’s height&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cqi&lt;/code&gt; is 1% of the container’s inline size&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cqb&lt;/code&gt; is 1% of the container’s block size&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cqmin&lt;/code&gt; and &lt;code&gt;cqmax&lt;/code&gt; are the smallest and largest (respectfully) value of either &lt;code&gt;cqi&lt;/code&gt; or &lt;code&gt;cqb&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, if we wanted to set something to be the 3% of the container’s width for example, we would set it to &lt;code&gt;3cqw&lt;/code&gt;. Scroll all the way down to line 123, 128 and 133, and you’ll see that the text elements are being set to a certain percentage of the container’s inline size. Try changing the size of the card and you’ll see that the font size grows and shrinks with the card. This might not be a real-world use case, but you get the gist 😁.&lt;/p&gt;

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

&lt;p&gt;So there you have it! Container Queries! How cool are they? You can use them to define responsive elements that react not based on the viewport’s size, but based on the space they’re given, regardless of the viewport size. That means that you can have two instances of the same component on a page, but because of the different space they’re given, they’ll appear differently. Check out the &lt;a href="https://creatures.dev/blog/all/1/" rel="noopener noreferrer"&gt;creatures.dev blog page&lt;/a&gt;. The featured post card is the same component as the post cards in the grid below. That card is the card from this example, but used in production. If you want to see more use cases, check out &lt;a href="https://css-tricks.com/a-few-times-container-size-queries-would-have-helped-me-out/" rel="noopener noreferrer"&gt;this article&lt;/a&gt; (takes you to CSS Tricks).&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting started with CSS Nesting</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Sat, 12 Aug 2023 21:48:34 +0000</pubDate>
      <link>https://dev.to/creatures-dev/getting-started-with-css-nesting-1b1k</link>
      <guid>https://dev.to/creatures-dev/getting-started-with-css-nesting-1b1k</guid>
      <description>&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/dr6UAQUAiu4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;CSS Nesting used to only be possible in CSS preprocessors like Sass and Less. But guess what, it's now built in native CSS and all modern browsers will support it after August 29th. Let's see what's CSS Nesting and how we can use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CSS Nesting?
&lt;/h2&gt;

&lt;p&gt;CSS Nesting is a new feature that allows you to nest selectors inside other selectors. Nesting helps by reducing repetition, reducing the file size, better file organization and easier refactoring. Let's see an example without nesting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* No nesting */&lt;/span&gt;
&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;lightgrey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;lightgrey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f1f3f4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&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 example can be written with nesting like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* With nesting */&lt;/span&gt;
&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;lightgrey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="err"&gt;header&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;lightgrey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f1f3f4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Nesting examples
&lt;/h2&gt;

&lt;p&gt;To get a good feel of how nesting works, let's see some more examples.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/nikolovlazar/embed/poQYdOG?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-level nesting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;lightgrey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="err"&gt;header&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;lightgrey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f1f3f4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="err"&gt;h2&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we can see that we can nest as many levels as we want. The first level is the &lt;code&gt;article.card&lt;/code&gt; selector, which is the root selector. The second level is the &lt;code&gt;&amp;amp; header&lt;/code&gt; selector, which is nested inside the root selector. The header styles will only be applied to &lt;code&gt;header&lt;/code&gt; elements that are children of &lt;code&gt;article.card&lt;/code&gt; elements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nesting without the &lt;code&gt;&amp;amp;&lt;/code&gt; symbol
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;lightgrey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="err"&gt;.content&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;lightgrey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We don't always need to add the &lt;code&gt;&amp;amp;&lt;/code&gt; symbol to indicate nesting. If we don't add it, CSS will add &lt;code&gt;&amp;amp;&lt;/code&gt; + " " (space) in front of the nested selector. By explicitly placing the &lt;code&gt;&amp;amp;&lt;/code&gt; symbol, we can control where the parent selector should be placed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nesting pseudo-classes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;

  &lt;span class="err"&gt;:is(.content,&lt;/span&gt; &lt;span class="err"&gt;footer)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's also possible to nest pseudo-classes like the &lt;code&gt;:is()&lt;/code&gt; in this example. As we saw in the previous example, if we don't add the &lt;code&gt;&amp;amp;&lt;/code&gt; symbol CSS is going to add that for us + an empty space, so this example will target all children of&lt;br&gt;
the &lt;code&gt;article.card&lt;/code&gt; element that are either &lt;code&gt;.content&lt;/code&gt; or &lt;code&gt;footer&lt;/code&gt; elements. The "compiled" selector will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="nd"&gt;:is&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.content&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;footer&lt;/span&gt;&lt;span class="o"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Nesting pseudo-elements
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;

  &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="err"&gt;footer&lt;/span&gt; &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* ... */&lt;/span&gt;

    &lt;span class="err"&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;maroon&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="err"&gt;&amp;amp;::after&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="err"&gt;}&lt;/span&gt;
  &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like the pseudo-classes, we can also nest pseudo-elements. In this example we're defining hover styles for the &lt;code&gt;a&lt;/code&gt; element that's a child of the &lt;code&gt;footer&lt;/code&gt;. We're also defining styles for the &lt;code&gt;::after&lt;/code&gt; pseudo-element of the &lt;code&gt;a&lt;/code&gt; element while it's being hovered. Feel free to play around with the example above to see the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nesting &lt;code&gt;@media&lt;/code&gt; queries
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* ... */&lt;/span&gt;

  &lt;span class="err"&gt;@media&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;768px&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* ... */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also nest &lt;code&gt;@media&lt;/code&gt; queries. This is a great way to keep the styles for a specific breakpoint in one place. This way we can easily find and update them if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;&amp;amp;&lt;/code&gt; symbol
&lt;/h2&gt;

&lt;p&gt;As mentioned in the previous examples, the &lt;code&gt;&amp;amp;&lt;/code&gt; symbol is used to indicate where the parent selector should be placed. This gives us more control over the final selector, and if needed, we can also completely rearrange the whole context. Let's see some examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;&amp;amp;:hover&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* a:hover */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* a :hover */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we can see the different output when we use the &lt;code&gt;&amp;amp;&lt;/code&gt; symbol and when we don't. In cases where we want to define pseudo-classes for the parent selector, we'd need to use the &lt;code&gt;&amp;amp;&lt;/code&gt; symbol. Otherwise, CSS will add the parent selector in front of the pseudo-class.&lt;/p&gt;

&lt;p&gt;Since the &lt;code&gt;&amp;amp;&lt;/code&gt; symbol is just a placeholder for the parent selector, we can also use it multiple times in the same selector, and flip the context completely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; &lt;span class="err"&gt;h2&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* header h2 */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* h2 header header header */&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Understanding the parser
&lt;/h2&gt;

&lt;p&gt;The nesting parser is what's responsible for parsing the nested selectors. The way that the parser identifies if the selector is defining a nested style is by checking whether the selector contains one of the symbols below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;amp; @ : . &amp;gt; ~ + # [ *
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might recognize some of these symbols. Some of them are used for selectors, pseudo-classes and pseudo-elements, combinators, attribute selectors etc...&lt;/p&gt;

&lt;p&gt;If the parser finds a nested selector that doesn't start with any of these symbols, it will fail to parse it, which will result in incorrect styles.&lt;/p&gt;

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

&lt;p&gt;Nesting is a great feature that can help us write more maintainable CSS. It can also help us write less code since we don't need to repeat the parent selector over and over again. This is huge for CSS, and I'm excited to see all of the creative ways developers are going to come up with to use it.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting Started with Jetpack Compose</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Fri, 03 Mar 2023 21:04:28 +0000</pubDate>
      <link>https://dev.to/sentry/getting-started-with-jetpack-compose-cpb</link>
      <guid>https://dev.to/sentry/getting-started-with-jetpack-compose-cpb</guid>
      <description>&lt;p&gt;Recently, we wrote about the &lt;a href="https://blog.sentry.io/2022/12/07/mobile-the-future-is-declarative/" rel="noopener noreferrer"&gt;demonstrative move to declarative UI&lt;/a&gt;. With &lt;a href="http://d.android.com/compose" rel="noopener noreferrer"&gt;Jetpack Compose&lt;/a&gt;, Android is joining the declarative trends. &lt;/p&gt;

&lt;p&gt;Jetpack Compose, a new declarative UI toolkit by Google made for building native Android apps, is rapidly gaining traction. In fact, as announced at the &lt;a href="https://www.youtube.com/watch?t=1069&amp;amp;v=Awi4J5-tbW4&amp;amp;feature=youtu.be" rel="noopener noreferrer"&gt;Android Dev Summit last year&lt;/a&gt; last year, 160 of the top 1,000 Android apps already use Jetpack Compose. In contrast to the traditional XML Views, Jetpack Compose allows you to build UIs using composable functions that describe how the UI should look and behave. &lt;/p&gt;

&lt;p&gt;The main advantage of using Jetpack Compose is that it allows you to write UI code that is more concise and easier to understand. This leads to improved maintainability and reduced development time. &lt;/p&gt;

&lt;p&gt;The main disadvantage of using Jetpack Compose is that it’s relatively new, so its ecosystem is limited and the number of available libraries, tools, and resources is lower than the traditional ecosystem. &lt;/p&gt;

&lt;p&gt;Despite that, we believe that learning Jetpack Compose is worth the learning curve and challenges. Here are some tips we've found helpful as you are getting started. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to start using Jetpack Compose
&lt;/h2&gt;

&lt;p&gt;The recommended IDE for working with Jetpack Compose is &lt;a href="https://developer.android.com/studio" rel="noopener noreferrer"&gt;Android Studio&lt;/a&gt;. After downloading and installing Android Studio, you’ll get the option to create a new project. To create a new Jetpack Compose application, you need to select either the &lt;code&gt;Empty Compose Activity&lt;/code&gt; (which uses Material v2), or &lt;code&gt;Empty Compose Activity (Material3)&lt;/code&gt; (which uses the Material v3 which is in version 1.0 as of last year). You can see both options in the top right of this screenshot: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzc211jlseavdxdgw7e2s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzc211jlseavdxdgw7e2s.png" alt="Creating a new Jetpack Compose app from Android Studio" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the easiest way to get started with Jetpack Compose. If you’d like to enable Jetpack Compose into an existing Android application, here’s what you need to do:&lt;/p&gt;

&lt;p&gt;1. Add the following build configurations in your app’s &lt;code&gt;build.gradle&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;buildFeatures&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// this flag enables Jetpack Compose&lt;/span&gt;
    &lt;span class="n"&gt;compose&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;composeOptions&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// the compiler version should match&lt;/span&gt;
    &lt;span class="c1"&gt;// your project's Kotlin version&lt;/span&gt;
    &lt;span class="n"&gt;kotlinCompilerExtensionVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.3.2"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. Add the Compose BOM (&lt;a href="https://developer.android.com/jetpack/compose/bom/bom" rel="noopener noreferrer"&gt;Bill of Materials&lt;/a&gt;) and the subset of Compose dependencies to your dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;composeBom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'androidx.compose:compose-bom:2023.01.00'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="n"&gt;composeBom&lt;/span&gt;
  &lt;span class="n"&gt;androidTestImplementation&lt;/span&gt; &lt;span class="n"&gt;composeBom&lt;/span&gt;

  &lt;span class="c1"&gt;// Choose one of the following:&lt;/span&gt;
  &lt;span class="c1"&gt;// Material Design 3&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.material3:material3'&lt;/span&gt;
  &lt;span class="c1"&gt;// or Material Design 2&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.material:material'&lt;/span&gt;
  &lt;span class="c1"&gt;// or skip Material Design and build directly on top of foundational components&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.foundation:foundation'&lt;/span&gt;
  &lt;span class="c1"&gt;// or only import the main APIs for the underlying toolkit systems,&lt;/span&gt;
  &lt;span class="c1"&gt;// such as input and measurement/layout&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.ui:ui'&lt;/span&gt;       

  &lt;span class="c1"&gt;// Android Studio Preview support&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.ui:ui-tooling-preview'&lt;/span&gt;
  &lt;span class="n"&gt;debugImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.ui:ui-tooling'&lt;/span&gt;

  &lt;span class="c1"&gt;// UI Tests&lt;/span&gt;
  &lt;span class="n"&gt;androidTestImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.ui:ui-test-junit4'&lt;/span&gt;
  &lt;span class="n"&gt;debugImplementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.ui:ui-test-manifest'&lt;/span&gt;

  &lt;span class="c1"&gt;// Optional - Included automatically by material, only add when you need&lt;/span&gt;
  &lt;span class="c1"&gt;// the icons but not the material library (e.g. when using Material3 or a&lt;/span&gt;
  &lt;span class="c1"&gt;// custom design system based on Foundation)&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.material:material-icons-core'&lt;/span&gt;
  &lt;span class="c1"&gt;// Optional - Add full set of material icons&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.material:material-icons-extended'&lt;/span&gt;
  &lt;span class="c1"&gt;// Optional - Add window size utils&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.material3:material3-window-size-class'&lt;/span&gt;

  &lt;span class="c1"&gt;// Optional - Integration with activities&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.activity:activity-compose:1.5.1'&lt;/span&gt;
  &lt;span class="c1"&gt;// Optional - Integration with ViewModels&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1'&lt;/span&gt;
  &lt;span class="c1"&gt;// Optional - Integration with LiveData&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.runtime:runtime-livedata'&lt;/span&gt;
  &lt;span class="c1"&gt;// Optional - Integration with RxJava&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.compose.runtime:runtime-rxjava2'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How do you build UI in Jetpack Compose?
&lt;/h2&gt;

&lt;p&gt;Jetpack Compose uses Composables to define the view hierarchy, and modifier to apply visual appearance and behavior changes to the composables they’re added to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Composable functions
&lt;/h3&gt;

&lt;p&gt;Composable functions (or just Composables) are ordinary Kotlin functions that are annotated with &lt;code&gt;@Composable&lt;/code&gt;, can be nested within another composable functions, and return a hierarchy of other composables in order to define their UI. Let’s see a simple composable that defines a contact row UI that contains a user photo, and a name and phone number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;ContactRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Row&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Image&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;painter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;painterResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drawable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;contentDescription&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"A photo of a user"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nc"&gt;Column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone&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;The &lt;code&gt;Row&lt;/code&gt; composable is a layout composable that renders its children one next to another. The &lt;code&gt;Image&lt;/code&gt; composable is the first child which is going to render the &lt;code&gt;user&lt;/code&gt; drawable. Then we have the &lt;code&gt;Column&lt;/code&gt; composable which, similar to the &lt;code&gt;Row&lt;/code&gt;, is a layout composable, but it renders its children one below another. The children of the &lt;code&gt;Column&lt;/code&gt; composable are two &lt;code&gt;Text&lt;/code&gt; composables that render the user’s name and phone number.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modifiers
&lt;/h3&gt;

&lt;p&gt;Modifiers are used to change the visual appearance and behavior of the composables they’re added to. We use modifiers when we want to change UI elements such as the size of the composable (width, height), the padding, background, or alignment.&lt;/p&gt;

&lt;p&gt;Modifiers can also be stacked on top of each other, allowing us to modify multiple visual properties. Here’s an example of how we can set the padding and max width of the Contact row from the previous snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;ContactRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxWidth&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How do you interact with data in Jetpack Compose?
&lt;/h2&gt;

&lt;p&gt;There are multiple ways to keep data within your Jetpack Compose app: &lt;a href="https://volcano-bovid-81c.notion.site/Getting-Started-with-Jetpack-Compose-7187d91a2f0c4e56969db56c51c91ec1" rel="noopener noreferrer"&gt;MutableState&lt;/a&gt;, &lt;a href="https://volcano-bovid-81c.notion.site/Getting-Started-with-Jetpack-Compose-7187d91a2f0c4e56969db56c51c91ec1" rel="noopener noreferrer"&gt;LiveData&lt;/a&gt;, and &lt;a href="https://volcano-bovid-81c.notion.site/Getting-Started-with-Jetpack-Compose-7187d91a2f0c4e56969db56c51c91ec1" rel="noopener noreferrer"&gt;StateFlow&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  MutableState
&lt;/h3&gt;

&lt;p&gt;In Jetpack Compose, state management can be accomplished by using the &lt;code&gt;remember&lt;/code&gt; API to store an object in memory, and the &lt;code&gt;mutableStateOf&lt;/code&gt; to declare a state variable. We can store both mutable and immutable objects. The &lt;code&gt;mutableStateOf&lt;/code&gt; creates an observable &lt;a href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/MutableState" rel="noopener noreferrer"&gt;&lt;code&gt;MutableState&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;, which is an observable type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;MutableState&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any changes to &lt;code&gt;value&lt;/code&gt; schedules a recomposition (re-rendering) of any composable functions that read it.&lt;br&gt;
There are three ways to declare a &lt;code&gt;MutableState&lt;/code&gt; object:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;val mutableState = remember { mutableStateOf(0) }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;var value by remember { mutableStateOf(false) }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;val (value, setValue) = remember { mutableStateOf("Hello, Compose!") }&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  LiveData
&lt;/h3&gt;

&lt;p&gt;LiveData is a data holder class that can be observed within a given lifecycle, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This ensures LiveData only updates observers that are in an active lifecycle state, which also ensures no memory leaks happen within your app.&lt;/p&gt;

&lt;p&gt;Let’s see an example of working with LiveData:&lt;br&gt;
1. You need to create an instance of the &lt;code&gt;LiveData&lt;/code&gt; class to hold a certain type of data, which is usually done within your &lt;a href="https://developer.android.com/reference/androidx/lifecycle/ViewModel" rel="noopener noreferrer"&gt;ViewModel&lt;/a&gt; class (use &lt;code&gt;MutableLiveData&lt;/code&gt; if you’d like to update the value at some point):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create a MutableLiveData instance that keeps a string&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableLiveData&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. Obtain the value in your composable by calling the &lt;code&gt;observeAsState&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;HomeScreen&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HomeViewModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create an observer of the state of userName&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observeAsState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// Use the value in your UI&lt;/span&gt;
  &lt;span class="nc"&gt;Column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&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;3. To update &lt;code&gt;userName&lt;/code&gt;'s value (also usually done in the view model), create a function that sets the new value to its &lt;code&gt;value&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;updateUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newName&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4. You’d use the new function in your Compose file as &lt;code&gt;viewModel.updateUserName("...")&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  StateFlow
&lt;/h3&gt;

&lt;p&gt;StateFlow is a newer alternative to LiveData. Both have similarities, and both are observable. Here’s how you can work with StateFlow in Jetpack Compose:&lt;br&gt;
1. Create an instance of &lt;code&gt;StateFlow&lt;/code&gt; to hold a certain type of data (use MutableStateFlow if you’d like to update the value at some point)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create a MutableStateFlow instance that keeps a string&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. Obtain the value in your composable by calling the &lt;code&gt;collectAsState&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;HomeScreen&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HomeViewModel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create an observer to collect the state of userName&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collectAsState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// Use the value in your UI&lt;/span&gt;
  &lt;span class="nc"&gt;Column&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&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;3. To update &lt;code&gt;userName&lt;/code&gt;'s value (also usually done in the view model), create a function that sets the new value to its &lt;code&gt;value&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;updateUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newName&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4. You’d use the new function in your Compose file as &lt;code&gt;viewModel.updateUserName("...")&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the best practices for Jetpack Compose?
&lt;/h2&gt;

&lt;p&gt;Aside from the &lt;a href="https://developer.android.com/jetpack/compose/performance/bestpractices" rel="noopener noreferrer"&gt;official best practices&lt;/a&gt; documentation, we’ve got a few additional tips that would make your codebase safer and easier to work in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code organization
&lt;/h3&gt;

&lt;p&gt;Every developer or organization has their own opinions on how a project should be structured. There is no “right” or “wrong” way to do it. Okay, maybe it’s wrong to put every file in one single directory 😅. Here’s an example structure to help you get started, which you can modify and evolve as your project grows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;.
├─ 📁 &lt;span class="gs"&gt;**ui**&lt;/span&gt; (to keep all your UI related things)
|  ├─ 📁 &lt;span class="gs"&gt;**screens**&lt;/span&gt; (where you define your screens composables and their corresponding view models)
|  |  └─ 📁 &lt;span class="gs"&gt;**home**&lt;/span&gt;
|  |     ├─ 📝 &lt;span class="gs"&gt;**HomeScreen.kt**&lt;/span&gt; (the UI for the Home screen)
|  |     └─ 📝 &lt;span class="gs"&gt;**HomeViewModel.kt**&lt;/span&gt; (the view model for the Home screen)
|  ├─ 📁 &lt;span class="gs"&gt;**components**&lt;/span&gt; (where you define components that are shared across multiple screens)
|  |  └─ 📝 &lt;span class="gs"&gt;**UserList.kt**&lt;/span&gt;
|  └─ 📁 &lt;span class="gs"&gt;**theme**&lt;/span&gt; (where you keep your theme definition and design tokens)
|     ├─ 📝 &lt;span class="gs"&gt;**Colors.kt**&lt;/span&gt;
|     ├─ 📝 &lt;span class="gs"&gt;**Shapes.kt**&lt;/span&gt;
|     ├─ 📝 &lt;span class="gs"&gt;**Theme.kt**&lt;/span&gt;
|     └─ 📝 &lt;span class="gs"&gt;**Typography.kt**&lt;/span&gt;
├─ 📁 &lt;span class="gs"&gt;**utils**&lt;/span&gt; (where you keep your various utility functions, like data converters etc...)
|  └─ 📝 &lt;span class="gs"&gt;**DateUtils.kt**&lt;/span&gt; 
└─ 📝 &lt;span class="gs"&gt;**MainActivity.kt**&lt;/span&gt; (this is your default MainActivity)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Avoid creating “god” files
&lt;/h3&gt;

&lt;p&gt;“God” files are a big no-no. They’re files that contain all code associated with them: UI, domain, business logic, utility functions etc… It might be easier putting everything into one file, but maintaining that would get harder and harder as you add functionalities. The solution to this is using a proper architecture in your Jetpack Compose app.&lt;/p&gt;

&lt;p&gt;There are multiple architectures that you can use, all with their own pros and cons. The most common one in Jetpack Compose is MVVM, abbreviated from Model-View-ViewModel, because Jetpack Compose has a first-class &lt;a href="https://developer.android.com/topic/libraries/architecture/viewmodel" rel="noopener noreferrer"&gt;&lt;code&gt;ViewModel&lt;/code&gt;&lt;/a&gt; implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stay true to the MVVM
&lt;/h3&gt;

&lt;p&gt;As you saw from the previous examples, Jetpack Compose has a first-class &lt;a href="https://developer.android.com/topic/libraries/architecture/viewmodel" rel="noopener noreferrer"&gt;&lt;code&gt;ViewModel&lt;/code&gt;&lt;/a&gt; implementation. The MVVM, or Model-View-ViewModel, is a software design pattern that is structured to separate business logic from the UI. That means, your UI should not handle state updates, but it should let the view model do that by sending it user actions.&lt;/p&gt;

&lt;p&gt;Let’s explore that with an example. Remember the &lt;code&gt;MutableStateFlow&lt;/code&gt; example from before? That example was oversimplified on purpose, but in a real-world project you would never expose a &lt;code&gt;MutableStateFlow&lt;/code&gt; from your &lt;code&gt;ViewModel&lt;/code&gt;, but just a &lt;code&gt;StateFlow&lt;/code&gt;. In order to make that work, you should define a private &lt;code&gt;MutableStateFlow&lt;/code&gt; variable and a public &lt;code&gt;StateFlow&lt;/code&gt; variable that returns the mutable flow by invoking the &lt;code&gt;asStateFlow()&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Create a private MutableStateFlow instance that keeps a string&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_userName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// Create a public StateFlow that returns the MutableStateFlow as immutable&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_userName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&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;With this simple change, we’re preventing the UI from being able to change the state. But, how do we actually change the state? We’ll expose a function from the view model that does that!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_userName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_userName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// Create a public function that updates the private MutableStateFlow value&lt;/span&gt;
  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_userName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newName&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now the UI has an immutable &lt;code&gt;StateFlow&lt;/code&gt; that it can observe, and a function to update its value. The business logic lives inside of the view model, while the Composable is only responsible to react to state changes and send user actions to the view model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don’t create a thousand flows
&lt;/h3&gt;

&lt;p&gt;So you’ve learned how to create state flows. Great! Would you repeat the same for every state variable you need in your UI? Please don’t 😅 To avoid that, you can create a &lt;code&gt;data class&lt;/code&gt; that keeps all of the values of your state, and create a single flow that uses it.&lt;/p&gt;

&lt;p&gt;Let’s learn this with an example. If we wanted to also keep the user’s phone number, email and address, we can create a data class called &lt;code&gt;HomeScreenState&lt;/code&gt; that contains all those values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;HomeScreenState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userPhone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we would refactor our view model to use the new &lt;code&gt;HomeScreenState&lt;/code&gt; instead of a &lt;code&gt;String&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_uiState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HomeScreenState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;uiState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HomeScreenState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And then we can use all of the values in our composable by &lt;code&gt;viewModel.uiState.userName&lt;/code&gt;. If we also wanted to be able to update all those values, we would create functions for each of them in our view model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_uiState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HomeScreenState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;uiState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HomeScreenState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;updateUserName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;userName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newName&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;updateUserEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newEmail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;userEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newEmail&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Keep a close eye on your errors and performance in production
&lt;/h3&gt;

&lt;p&gt;As you're getting acclimated to Jetpack Compose, an error and performance monitoring tool can be really helpful to reduce your learning curve and ensure that your app is bug-free. Jetpack Compose does a lot of heavy lifting for developers – as a declarative toolkit, developers need to write less code to describe their UI, and Jetpack Compose takes care of the rest. But it does abstract away a lot of code, making it difficult to identify errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sentry.io/for/android/?utm_medium=website&amp;amp;utm_source=jetpack-compose-getting-started&amp;amp;utm_campaign=android&amp;amp;utm_content=blog&amp;amp;utm_term=" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt; offers an out-of-the-box integration that can help you build a better Jetpack Compose app. The integration gives precise context to reduce troubleshooting time with transactions and breadcrumbs. Keep an eye on all the issues and crashes your app is experiencing in production, with a lot of context as to why the issue happened, the exact line of code that triggered it, and all sorts of hardware and software info of the device it ran.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzowc7j7wb86pi5wn876.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzowc7j7wb86pi5wn876.png" alt="Android app monitored in Sentry" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;I’d totally understand if you’re feeling overwhelmed by now, but let’s do a quick recap! We’ve learned how to create a new Jetpack Compose project, and that Jetpack Compose uses Composables and Modifiers to define the view hierarchy and apply visual changes. Data in Jetpack Compose can be handled either with a &lt;code&gt;MutableState&lt;/code&gt;, &lt;code&gt;LiveData&lt;/code&gt;, or &lt;code&gt;StateFlow&lt;/code&gt;, which make the composables that observe it re-render when the value changes, making our UI dynamic. We also learned how to keep our projects tidy, and how to write maintainable composables and view models.&lt;/p&gt;

&lt;p&gt;Even though it’s a relatively new technology, Jetpack Compose’s ecosystem is steadily growing, so we can expect to see a lot of libraries pop up that make it easier to create Jetpack Compose apps. With companies like Lyft, Twitter, Airbnb, Square, Reddit, and Firefox putting their trust into it, more and more developers will follow along and create apps, libraries and resources for Jetpack Compose.&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Mobile: The Future is Declarative</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Fri, 30 Dec 2022 10:55:05 +0000</pubDate>
      <link>https://dev.to/sentry/mobile-the-future-is-declarative-1bc9</link>
      <guid>https://dev.to/sentry/mobile-the-future-is-declarative-1bc9</guid>
      <description>&lt;p&gt;The mobile development ecosystem has always been very diverse, arguably more diverse than the web development ecosystem. While it seems like every day there are more frameworks and tools for web developers, a lot of them are built on top of JavaScript and implement similar patterns to each other. The mobile ecosystem, on the other hand, has a core set of languages that make the differences between mobile tools and frameworks much easier to identify.&lt;/p&gt;

&lt;p&gt;Two of the leading native mobile platforms are native Android and iOS, both of which have had interesting innovations recently. With the introductions of Jetpack Compose and SwiftUI, developing native apps looks very similar to developing React Native or Flutter apps. Both React Native and Flutter have a declarative approach from the start, but with Android and iOS now joining the declarative bandwagon, we can see that the future of mobile development is declarative.&lt;/p&gt;

&lt;h2&gt;
  
  
  A little history
&lt;/h2&gt;

&lt;p&gt;While React Native and Flutter have always taken a declarative approach, Android and iOS started completely different.&lt;/p&gt;

&lt;p&gt;Android utilized (and still does) Views. Views are XML files where we define our user interfaces using widgets like Button, TextView, LinearLayout and where we assign IDs to those widgets. Those IDs are then used to reference widgets in our Java files where we develop the functionality and behavior. Here’s an example View file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;LinearLayout&lt;/span&gt;
  &lt;span class="na"&gt;xmlns:android=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt;
  &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"match_parent"&lt;/span&gt;
  &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"match_parent"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;TextView&lt;/span&gt;
    &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/text_view_id"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"wrap_content"&lt;/span&gt;
    &lt;span class="na"&gt;android:text=&lt;/span&gt;&lt;span class="s"&gt;"@string/hello"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/LinearLayout&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we would create a reference to the widget like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Activity&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bundle&lt;/span&gt; &lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onCreate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;setContentView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;activity_main&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a reference to the TextView using its ID&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;TextView&lt;/span&gt; &lt;span class="n"&gt;helloTextView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TextView&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;findViewById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;text_view_id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Change the TextView's text&lt;/span&gt;
    &lt;span class="n"&gt;helloTextView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;user_greeting&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;iOS did have some declarative features, like Auto Layout, UIAppearance, the Objective-C @property declarations, KVC collection operators, and Combine, but it still required writing some level of imperative code.&lt;/p&gt;

&lt;p&gt;For example, iOS had (and still has) Storyboards. Storyboards is a graphical tool we use to build our UIs. It is actually an XML file under the hood, but the developers almost never touch the XML code itself. Here’s how we added UI elements in our Storyboards, and created references and actions:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/pTTpjeSARZw"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Becoming declarative
&lt;/h2&gt;

&lt;p&gt;To refresh our memory, the &lt;strong&gt;imperative&lt;/strong&gt; approach is when you provide step-by-step instructions until you achieve the desired UI. The &lt;strong&gt;declarative&lt;/strong&gt; approach is when you describe how the final state of the desired UI should look.&lt;/p&gt;

&lt;p&gt;Android’s new Jetpack Compose is written in Kotlin, which is a programming language as opposed to XML, which is a markup language. The previous XML View example would look something like this in Jetpack Compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ComponentActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;// Start defining the UI&lt;/span&gt;
    &lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;BasicsCodelabTheme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Add a Row (alternative to LinearLayout)&lt;/span&gt;
        &lt;span class="nc"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Add a Text (alternative to TextView)&lt;/span&gt;
          &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Sentry!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, building UIs with this approach requires a lot less code. And, since we stay in a programming language context, we can directly use variables and callbacks instead of creating references to widgets and attaching logic to those widgets. Any change in the values will trigger a recomposition (rerender), so our UI is always up to date.&lt;/p&gt;

&lt;p&gt;iOS’s SwiftUI is pretty much the same, just built with Swift instead. The previous Storyboard example would look something like this in SwiftUI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// handle onClick&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Button"&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;Again, much less code. And we’re in a programming language, so we can directly define the Button’s label and provide an &lt;code&gt;onClick&lt;/code&gt; callback without creating a reference.&lt;/p&gt;

&lt;p&gt;One issue we stumbled upon when working with the imperative UIKit is activating constraints of a view before adding it to the view hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;subview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UIView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;subview&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leadingAnchor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constraint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;equalTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leadingAnchor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="c1"&gt;// ^^^ it's going to crash on this line with the error:&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// Unable to activate constraint with anchors *** because they have&lt;/span&gt;
&lt;span class="c1"&gt;// no common ancestor. Does the constraint or its anchors reference&lt;/span&gt;
&lt;span class="c1"&gt;// items in different view hierarchies?  That's illegal.&lt;/span&gt;

&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addSubview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subview&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swapping the &lt;code&gt;subview.leadingAnchor.constraint(...)&lt;/code&gt; line with the &lt;code&gt;view.addSubview(view)&lt;/code&gt; line will work without an error. Admittedly, it took me a while to understand what was going on when I first encountered this error, and I triggered this error a few more times until it became a muscle memory for me. But with the new declarative approach we’re not going to encounter this issue. (Muscle memory in coding is good, but it’s better to improve the developer experience).&lt;/p&gt;

&lt;p&gt;This shift towards declarative in Android and iOS is a huge step towards better developer experience and faster development. Defining your UI in a declarative way using a programming language solves a lot of the pain points that Android and iOS developers have.&lt;/p&gt;

&lt;p&gt;Here are some of the benefits of the declarative approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Theming becomes easier and more dynamic.&lt;/li&gt;
&lt;li&gt;State Management feels natural and actually plays a &lt;strong&gt;crucial role&lt;/strong&gt; in the new declarative approach.&lt;/li&gt;
&lt;li&gt;The composition approach allows us to compose our UI by nesting components, which is inferred from the nesting of the code’s block scope.&lt;/li&gt;
&lt;li&gt;Dynamic layouts and conditional rendering are now straightforward because we’re building our UIs with programming languages that have control structures and branching logic.&lt;/li&gt;
&lt;li&gt;It’s easier to &lt;code&gt;grep&lt;/code&gt; through Swift/Kotlin code than through XML.&lt;/li&gt;
&lt;li&gt;We can more uniformly refactor our code using the same tools that apply to the rest of the programming language.&lt;/li&gt;
&lt;li&gt;The code diffs in PRs are easier to understand.&lt;/li&gt;
&lt;li&gt;Since our UI elements are built with actual data structures (i.e. functions, classes, structs) as opposed to markup language, we have the possibility of doing unit tests on our Views as well (not available for Jetpack Compose at the time of writing this blog post).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, there are always some drawbacks to be aware of. It’s worth mentioning that both SwiftUI and Jetpack Compose are only 2-3 years old. A short research on their drawbacks will reveal the lack of documentation, smaller community, some performance issues (for example Android’s lazy columns), not all components from the previous frameworks are supported, and there are limitations when it comes to building more complex UIs. Though they are still evolving and improving, be mindful of the pain points if you decide to start working with them today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example UI
&lt;/h2&gt;

&lt;p&gt;Let’s look at how the same example UI is built in the declarative approach for both iOS and Android:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabevkardb0amesq5js7g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabevkardb0amesq5js7g.jpeg" alt="An example UI on both iOS and Android platforms" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;iOS (SwiftUI):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;//: An example Modal UI built with SwiftUI&lt;/span&gt;

&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;SwiftUI&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;PlaygroundSupport&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;emailAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;VStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;systemName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"envelope.fill"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;largeTitle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sign up to our newsletter!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Since you love our content so much, why not get them every day in your inbox?"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;multilineTextAlignment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;center&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;HStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"john@doe.xyz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;$emailAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;border&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="kt"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// Handle onClick logic&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Subscribe"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;white&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;horizontal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vertical&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;black&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Present the view controller in the Live View window&lt;/span&gt;
&lt;span class="kt"&gt;PlaygroundPage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLiveView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ContentView&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;a href="https://gist.github.com/nikolovlazar/4cbfa053b0d866f9e7a47ee51157357a" rel="noopener noreferrer"&gt;View iOS gist here&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Android (Jetpack Compose):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;Modal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;horizontalAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Alignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CenterHorizontally&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&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="nc"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;Icons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Filled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;246&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// similar to the iOS one&lt;/span&gt;
            &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;48&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Sign up to our newsletter!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fontWeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FontWeight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fontSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Since you love our content so much, why not get them every day in your inbox?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;textAlign&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TextAlign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Gray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fontSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nc"&gt;Row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;verticalAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Alignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CenterVertically&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&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="nc"&gt;TextField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;onValueChange&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
                &lt;span class="n"&gt;placeholder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"john@doe.xyz"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TextFieldDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textFieldColors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;backgroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;White&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;border&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderStroke&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="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Black&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;White&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxHeight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* handle onClick logic */&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ButtonDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buttonColors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;backgroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;contentColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;White&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;contentPadding&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PaddingValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;horizontal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vertical&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;shape&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RoundedCornerShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxHeight&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="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Subscribe"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;a href="https://gist.github.com/nikolovlazar/93f786e34cbf62d429908d0dd6c00d60" rel="noopener noreferrer"&gt;View Android gist here&lt;/a&gt;)&lt;/p&gt;

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

&lt;p&gt;The declarative approach of building UIs brings us a ton of benefits and eliminates a lot of the “pain points” we had in the imperative approach, but it also introduces new ones. Building UIs requires a lot less time with the declarative approach, and a lot less code. It’s also easier to achieve dynamic layouts and conditional rendering.&lt;/p&gt;

&lt;p&gt;Since the native platforms are taking this approach, it also sets up the whole ecosystem for creating new frameworks on top of the native ones that will bring a lot more features and possibilities. It might be a bit early to call them “the industry standard” yet, but the mobile development world is about to get even more productive.&lt;/p&gt;

</description>
      <category>deno</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Measuring application performance in Swift using transactions</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Tue, 22 Nov 2022 21:43:00 +0000</pubDate>
      <link>https://dev.to/sentry/measuring-application-performance-in-swift-using-transactions-22dp</link>
      <guid>https://dev.to/sentry/measuring-application-performance-in-swift-using-transactions-22dp</guid>
      <description>&lt;p&gt;So you're building a mobile app that’s performing big data requests; or crunching big data. But now you're asking yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How will my app perform in production?&lt;/li&gt;
&lt;li&gt;How will it perform on a lower-tier phone?&lt;/li&gt;
&lt;li&gt;Is there a scenario where the function's execution time is unbearably long?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Sentry’s &lt;a href="https://docs.sentry.io/platforms/apple/guides/ios/performance/instrumentation/custom-instrumentation/?utm_medium=website&amp;amp;utm_source=blog&amp;amp;utm_campaign=mobile-swift&amp;amp;utm_content=&amp;amp;utm_term=" rel="noopener noreferrer"&gt;Custom Instrumentation&lt;/a&gt; you can keep an eye on those big data-handling functions. Let’s see how you can implement them in your Storyboard and SwiftUI projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;First, you need to setup &lt;a href="https://docs.sentry.io/product/performance/?utm_medium=website&amp;amp;utm_source=blog&amp;amp;utm_campaign=mobile-swift&amp;amp;utm_content=&amp;amp;utm_term=" rel="noopener noreferrer"&gt;performance monitoring&lt;/a&gt;. With performance monitoring, Sentry tracks application performance, measures metrics like throughput and latency, and displays the impact of errors across multiple services. &lt;/p&gt;

&lt;p&gt;To get your app setup with performance monitoring, you need to configure the traces sample rate in your &lt;code&gt;Swift.UI.App&lt;/code&gt; file's &lt;code&gt;init()&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Sentry&lt;/span&gt;

&lt;span class="c1"&gt;// This should be in your SwiftUI.App file's init() method&lt;/span&gt;
&lt;span class="kt"&gt;SentrySDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dsn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"[YOUR_DSN_HERE]"&lt;/span&gt;

  &lt;span class="c1"&gt;// Example uniform sample rate: capture 100% of transactions&lt;/span&gt;
  &lt;span class="c1"&gt;// In Production you will probably want a smaller number such as 0.5 for 50%&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tracesSampleRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;

  &lt;span class="c1"&gt;// OR if you prefer, determine traces sample rate based on the&lt;/span&gt;
  &lt;span class="c1"&gt;// sampling context&lt;/span&gt;
  &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tracesSampler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="c1"&gt;// Don't miss any transactions for VIP users &lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;?[&lt;/span&gt;&lt;span class="s"&gt;"vip"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt; &lt;span class="c1"&gt;// 25% for everything else&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing custom transactions
&lt;/h2&gt;

&lt;p&gt;Now that you’ve got performance monitoring enabled, you can start setting up custom transactions in the functions you’d like to measure. The measurement starts when you start a new transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// don't forget to import Sentry's SDK at the top&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Sentry&lt;/span&gt;

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

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;syncPersonDatabase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SentrySDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// Give the transaction a descriptive name&lt;/span&gt;
    &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"transaction-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Give the operation a descriptive name&lt;/span&gt;
    &lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"transaction-operation"&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="c1"&gt;// perform the operation&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, let’s say you want to measure how long it takes to sync a database of all people within a company; the “person database”. The name and operation could be &lt;code&gt;"Sync person database"&lt;/code&gt; and &lt;code&gt;"data.update"&lt;/code&gt; respectfully.&lt;/p&gt;

&lt;p&gt;When you’re done with the operation you want to measure, you can call the &lt;code&gt;finish()&lt;/code&gt; method of the transaction. Finishing the transaction will stop the measurement and send it to your Sentry project.&lt;/p&gt;

&lt;p&gt;The final structure of your function should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// don't forget to import Sentry's SDK at the top&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Sentry&lt;/span&gt;

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

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;syncPersonDatabase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SentrySDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Sync person database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// give it a name&lt;/span&gt;
    &lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"data.update"&lt;/span&gt; &lt;span class="c1"&gt;// and a name for the operation&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="c1"&gt;// perform the operation&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// stop the measurement and report it to Sentry&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring the performance
&lt;/h2&gt;

&lt;p&gt;So far you’ve set up the performance measuring mechanism of your &lt;code&gt;syncPersonDatabase&lt;/code&gt; function. Now you run your app...&lt;/p&gt;

&lt;p&gt;You run it again...&lt;/p&gt;

&lt;p&gt;Maybe you run it one more time for good measure...&lt;/p&gt;

&lt;p&gt;Okay, that should have kicked off a transaction. You visit your Sentry dashboard and open the Performance tab and see the new custom transaction appear at the bottom:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsaxyh11b9thrpyqyyiw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsaxyh11b9thrpyqyyiw.png" alt="Transactions Table" width="800" height="85"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on the transaction and then into the “Suspect Span” found in the second table, you will see a detailed representation of the span operation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4m7rl1b67wi5571g842m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4m7rl1b67wi5571g842m.png" alt="Span Summary" width="800" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's break this view down a bit.&lt;/p&gt;

&lt;p&gt;The first thing that you’ll see is the Self Time Breakdown chart. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k7idnl9j9n62lp3ynb0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k7idnl9j9n62lp3ynb0.png" alt="Span Summary Breakdown" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This chart visualises the p50, p75, p95 and p99 metrics, which are called “latency percentiles”. p50 means that 50% of the function executions are faster than the p50 value (let’s say 0.47ms). To learn more about the latency percentiles, check out the &lt;a href="https://docs.sentry.io/product/performance/metrics/#latency" rel="noopener noreferrer"&gt;Latency section&lt;/a&gt; in the Metrics documentation. &lt;/p&gt;

&lt;p&gt;In the top right corner you can see the Self Time Percentiles widgets. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9h8359y6hrv3sfd1ivxl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9h8359y6hrv3sfd1ivxl.png" alt="Span Summary Self Time Percentiles" width="633" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don’t want to look at the chart, these values will give you an insight on the average “p” values.&lt;/p&gt;

&lt;p&gt;Below the chart is the events table. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xp3eu4yaoef77ufnabw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xp3eu4yaoef77ufnabw.png" alt="Span Summary Events Table" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here if you notice an event that took longer than you anticipated, you can click on it to get more details about it, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The device that the user was using when the transaction occurred&lt;/li&gt;
&lt;li&gt;The device’s memory at the time of the transaction&lt;/li&gt;
&lt;li&gt;How long the user had been using the app prior to the transaction&lt;/li&gt;
&lt;li&gt;The version of the app&lt;/li&gt;
&lt;li&gt;The breadcrumbs (bits of events and interactions that led up to that transaction)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you enough information on the context and environment to be able to discover potential ways to refactor the function to improve its performance. And, you can get even more granular if your function has multiple steps of execution. You can measure each steps individually and still keep them under the umbrella of the main function transaction. &lt;/p&gt;

&lt;p&gt;Enter: &lt;strong&gt;child spans&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  More granular measurement with child spans
&lt;/h2&gt;

&lt;p&gt;Child spans are like mini transactions that are attached to the main transaction. And child spans can have their own child spans as well. This feature allows you to measure your function’s performance in a greater detail, by starting a child span for every step of the function.&lt;/p&gt;

&lt;p&gt;Let’s say your function performs the following steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;1.&lt;/span&gt; Gather changed person properties
&lt;span class="p"&gt;2.&lt;/span&gt; Pass each of them through a validation mechanism
&lt;span class="p"&gt;3.&lt;/span&gt; Perform the updates to the database
&lt;span class="p"&gt;4.&lt;/span&gt; Save the current timestamp as the "lastUpdated"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can measure each step individually using child spans like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// don't forget to import Sentry's SDK at the top&lt;/span&gt;
&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Sentry&lt;/span&gt;

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

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;syncPersonDatabase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SentrySDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Sync person database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// give it a name&lt;/span&gt;
    &lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"data.update"&lt;/span&gt; &lt;span class="c1"&gt;// and a name for the operation&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// span the first child&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;gatherSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"gather-changed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// perform the gather operation&lt;/span&gt;
  &lt;span class="n"&gt;gatherSpan&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// span the second child&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;validationSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"validate-changed"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// perform the validation&lt;/span&gt;
  &lt;span class="n"&gt;validationSpan&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// span the third child&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;updateSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"update-database"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// perform the update&lt;/span&gt;
  &lt;span class="n"&gt;updateSpan&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// span the last child&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;timestampSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"update-timestamp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// perform the timestamp update&lt;/span&gt;
  &lt;span class="n"&gt;timestampSpan&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&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;Remember: Only finished spans will be sent along with the transaction. So you need to make sure that you’re finishing each child span before finishing the main transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring the performance of child spans
&lt;/h2&gt;

&lt;p&gt;Alright, you have set up child spans and you're ready to run your app again with the latest changes...&lt;/p&gt;

&lt;p&gt;You run it again...&lt;/p&gt;

&lt;p&gt;Maybe you run it one more time for good measure...&lt;/p&gt;

&lt;p&gt;Done! You head back to your Sentry dashboard and notice that the Suspect Spans table provides more information now. You can see the child spans, and how long they took to execute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzg6kb5di7clgbnk4htl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzg6kb5di7clgbnk4htl.png" alt="Suspect Spans Table" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clicking on the “View All Spans” button you can see and sort all of them.&lt;/p&gt;

&lt;p&gt;But you realize that your function has conditional steps that don’t always execute. Or maybe you have dynamically generated child spans whose name contains a unique id. To see the actual order of the child spans, you have to pick a specific event from the Events Table above and a new page will open with more details around that specific event.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnie7owzawf8r253wenc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnie7owzawf8r253wenc8.png" alt="Event Details" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see from the Gantt chart the order of execution of the child spans. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6ynm1wuayvao2l6cw44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy6ynm1wuayvao2l6cw44.png" alt="Event Details Gantt Chart" width="800" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can see that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;data.update&lt;/code&gt; encapsulates all child spans&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;gather-changed&lt;/code&gt; started executing first and took 16.02ms&lt;/li&gt;
&lt;li&gt;Then the &lt;code&gt;validate-changed&lt;/code&gt; ran for 5.64ms&lt;/li&gt;
&lt;li&gt;Then the &lt;code&gt;update-database&lt;/code&gt; took its 121.02ms to run&lt;/li&gt;
&lt;li&gt;And finally the &lt;code&gt;update-timestamp&lt;/code&gt; flashed for 5.63ms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can identify which step contributes the most to the performance of your function with this. You can now turn our whole focus to improving the performance of your &lt;code&gt;update-database&lt;/code&gt; function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a transaction in multiple functions
&lt;/h2&gt;

&lt;p&gt;Now that you have a hang of measuring straightforward functions, you're ready to tackle something a bit more complex. You realize that you want to measure performance on multiple nested functions. You do not need to pass the transaction as an argument. Instead, you can bind the transaction to the current scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SentrySDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Sync person database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// give it a name&lt;/span&gt;
  &lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"data.update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// and a name for the operation&lt;/span&gt;
  &lt;span class="nv"&gt;bindToScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// bind the transaction to the curent scope&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can access this transaction without having to pass it as an argument while you're in the current scope. You could have a function &lt;code&gt;gatherChangedProperties&lt;/code&gt;, that calls another function &lt;code&gt;filterProperties&lt;/code&gt;. And to gain access to the transaction within the &lt;code&gt;filterProperties&lt;/code&gt;, you can directly reference &lt;code&gt;SentrySDK.span&lt;/code&gt;, and if no transaction exists yet, you can create it. Your structure should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SentrySDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Sync person database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// give it a name&lt;/span&gt;
  &lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"data.update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// and a name for the operation&lt;/span&gt;
  &lt;span class="nv"&gt;bindToScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// bind the transaction to the curent scope&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;gatherChangedProperties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

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

&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;gatherChangedProperties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;filteredProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;filterProperties&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;filterProperties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// obtain the transaction&lt;/span&gt;
  &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SentrySDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;

  &lt;span class="c1"&gt;// if non existing, recreate it&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;SentrySDK&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// use it like a normal transaction&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;filterSpan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"filter-properties"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You can now create custom transactions to measure long running functions in your app with &lt;code&gt;SentrySDK.startTransaction(...)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You can measure more granularly by starting child spans to our transaction for each logical step in our function with &lt;code&gt;transaction.startChild(...)&lt;/code&gt; to zero in on the slowest part of the function and improve it. &lt;/li&gt;
&lt;li&gt;If you’re measuring a more complex function with multiple branches, you can bind the transaction to the current scope by setting the &lt;code&gt;bindToScope&lt;/code&gt; property to &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Time to measure all the things! 🙌&lt;/p&gt;

</description>
      <category>swift</category>
      <category>sentry</category>
      <category>performance</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Generating blurDataURL for remote images in Next.js</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Sun, 19 Dec 2021 12:18:14 +0000</pubDate>
      <link>https://dev.to/nikolovlazar/generating-blurdataurl-for-remote-images-in-nextjs-51mg</link>
      <guid>https://dev.to/nikolovlazar/generating-blurdataurl-for-remote-images-in-nextjs-51mg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted at: &lt;a href="https://nikolovlazar.com/blog/generating-blur-for-dynamic-images-nextjs" rel="noopener noreferrer"&gt;nikolovlazar.com/blog/generating-blur-for-dynamic-images-nextjs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Next.js &lt;a href="https://nextjs.org/docs/basic-features/image-optimization" rel="noopener noreferrer"&gt;Image Component&lt;/a&gt; is IMO the best tool that you can use to ensure the images&lt;br&gt;
on your Next.js website are optimized, and your page loads quicker. One interesting feature that the &lt;code&gt;next/image&lt;/code&gt; component&lt;br&gt;
provides is the &lt;code&gt;placeholder&lt;/code&gt; prop, whose values can be either &lt;code&gt;blur&lt;/code&gt; or &lt;code&gt;empty&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When the placeholder is set to &lt;code&gt;blur&lt;/code&gt;, we need to provide the &lt;code&gt;blurDataURL&lt;/code&gt;. If we're importing local images statically, Next.js&lt;br&gt;
can access the resource and generate the &lt;code&gt;blurDataURL&lt;/code&gt; for us. But, when we want to add the blur effect to remote images there&lt;br&gt;
are a few things that we need to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register the provider's domain in &lt;code&gt;next.config.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Generate the &lt;code&gt;blurDataURL&lt;/code&gt; and pass it to the &lt;code&gt;NextImage&lt;/code&gt; component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm using &lt;a href="https://mdxjs.com/" rel="noopener noreferrer"&gt;MDX&lt;/a&gt; for the content of my website (this one!), so in this article I'll explain the &lt;code&gt;blurDataURL&lt;/code&gt;&lt;br&gt;
generation integrated with MDX, but the functionality is generic and not tied with MDX in any way. So let's begin!&lt;/p&gt;
&lt;h2&gt;
  
  
  Registering provider domains
&lt;/h2&gt;

&lt;p&gt;First things first, you need to register the provider's domain in order to render remote images with &lt;code&gt;next/image&lt;/code&gt;. In my case,&lt;br&gt;
I'm loading the &lt;code&gt;og:image&lt;/code&gt; from GitHub, and the URL looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://opengraph.githubassets.com/f4a95bd3aa5113a1f599f5a810edeb16b885f3364b0443dc3c34a02c3290a5d8/chakra-ui/chakra-ui-docs/pull/154
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By looking at the URL, we know that we need to register the &lt;code&gt;opengraph.githubassets.com&lt;/code&gt; domain, so let's jump in the &lt;code&gt;next.config.js&lt;/code&gt;&lt;br&gt;
and do that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next.config.js&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;domains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opengraph.githubassets.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Now that we've got out of the way, let's start generating the &lt;code&gt;blurDataURL&lt;/code&gt; prop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate blurDataURL
&lt;/h2&gt;

&lt;p&gt;Since I'm using MDX and I'm rendering the pages statically, I've added a simple plugin that filters out all of&lt;br&gt;
the images from the markdown, calculates their &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, and &lt;code&gt;blurDataURL&lt;/code&gt; and passes them as props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/utils/plugins/image-metadata.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;imageSize&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;image-size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ISizeCalculationResult&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;image-size/dist/types/interface&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&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;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getPlaiceholder&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;plaiceholder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Node&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;unist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;visit&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;unist-util-visit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;promisify&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;util&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Convert the imageSize method from callback-based to a Promise-based&lt;/span&gt;
&lt;span class="c1"&gt;// promisify is a built-in nodejs utility function btw&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sizeOf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;promisify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The ImageNode type, because we're using TypeScript&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ImageNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;tagName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&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="nl"&gt;width&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="nl"&gt;blurDataURL&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blur&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;empty&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="c1"&gt;// Just to check if the node is an image node&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isImageNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;ImageNode&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;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ImageNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tagName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImageNode&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ISizeCalculationResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;blur64&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Check if the image is external (remote)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isExternal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// If it's local, we can use the sizeOf method directly, and pass the path of the image&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isExternal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Calculate image resolution (width, height)&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sizeOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// Calculate base64 for the blur&lt;/span&gt;
    &lt;span class="nx"&gt;blur64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPlaiceholder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// If the image is external (remote), we'd want to fetch it first&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Convert the HTTP result into a buffer&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;imageRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Calculate the resolution using a buffer instead of a file path&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;imageSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Calculate the base64 for the blur using the same buffer&lt;/span&gt;
    &lt;span class="nx"&gt;blur64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getPlaiceholder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// If an error happened calculating the resolution, throw an error&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Invalid image with src "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// add the props in the properties object of the node&lt;/span&gt;
  &lt;span class="c1"&gt;// the properties object later gets transformed as props&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blurDataURL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;blur64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;placeholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blur&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageMetadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Node&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;// Create an array to hold all of the images from the markdown file&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImageNode&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;element&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&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;// Visit every node in the tree, check if it's an image and push it in the images array&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isImageNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;for &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;image&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Loop through all of the images and add their props&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;addProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

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

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

&lt;/div&gt;



&lt;p&gt;That's all we need to do to calculate the &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, and &lt;code&gt;blurDataURL&lt;/code&gt; props. In order to use this&lt;br&gt;
plugin, let's jump to the &lt;code&gt;pages/blog/[slug].tsx&lt;/code&gt; page that renders the blog post itself:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GetStaticProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&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;// get the post slug from the params&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// get the post content. readBlogPost just reads the file contents using fs.readFile(postPath, 'utf8')&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;postContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readBlogPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Use the gray-matter package to isolate the markdown matter (title, description, date) from the content&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;matter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// use the serialize method from the 'next-mdx-remote/serialize' package to compile the MDX&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;mdxOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// pass the imageMetadata utility function we just created&lt;/span&gt;
          &lt;span class="na"&gt;rehypePlugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;imageMetadata&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! To see this in action, put a &lt;code&gt;console.log&lt;/code&gt; in your MDX Image component and check the props.&lt;br&gt;
Here's my MDX Image component:&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;const&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NextImage&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;layout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;responsive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lazy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;props&lt;/code&gt; object is actually the &lt;code&gt;node.properties&lt;/code&gt; object in the &lt;code&gt;image-metadata.ts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If you've followed along the article, you should already see the blur effect happening.&lt;/p&gt;

&lt;p&gt;This solution can also be applied in different scenarios other than MDX. Just bear in mind that obtaining&lt;br&gt;
the image data (the &lt;code&gt;!isExternal&lt;/code&gt; if statement in &lt;code&gt;image-metadata.ts&lt;/code&gt;) is a server-side functionality,&lt;br&gt;
because it uses Node.JS's &lt;code&gt;fs&lt;/code&gt; package. If for some reason you need to do this on the client-side you&lt;br&gt;
need to change the way you get the image data.&lt;/p&gt;

&lt;p&gt;If you want to see the whole system in place, make sure to check out the source of my website: &lt;a href="https://github.com/nikolovlazar/nikolovlazar.com" rel="noopener noreferrer"&gt;nikolovlazar/nikolovlazar.com&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: if you're applying the blur effect on user submitted images, make sure you know where those images&lt;br&gt;
will be stored, and don't forget to register the domain in the &lt;code&gt;next.config.js&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>nextjs</category>
    </item>
    <item>
      <title>A simple change improved Chakra UI's PageSpeed significantly</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Sat, 18 Dec 2021 18:57:50 +0000</pubDate>
      <link>https://dev.to/nikolovlazar/a-simple-change-improved-chakra-uis-pagespeed-significantly-5689</link>
      <guid>https://dev.to/nikolovlazar/a-simple-change-improved-chakra-uis-pagespeed-significantly-5689</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally posted at &lt;a href="https://nikolovlazar.com/blog/improving-chakra-ui-page-speed" rel="noopener noreferrer"&gt;nikolovlazar.com/blog/improving-chakra-ui-page-speed&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've noticed that the PageSpeed scores for &lt;a href="https://chakra-ui.com" rel="noopener noreferrer"&gt;https://chakra-ui.com&lt;/a&gt; were not that great. I know that&lt;br&gt;
we've built the page using the best practices, but for some reason the scores weren't what we expected. I took some time&lt;br&gt;
to analyze the metrics and noticed that the TTI (Time to Interactive) and TBT (Total Blocking Time) were crazy high!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4r4u7dw32lpey97n1on.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4r4u7dw32lpey97n1on.png" alt="Chakra UI's Old PageSpeed Metrics" width="800" height="751"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scrolling down in the Diagnostics section I noticed the "Avoid enormous network payloads" issue, notifying me that the total&lt;br&gt;
size of the network payload was 7,053 KiB, and that's way too much! Clicking on the issue revealed that 9 out of 10 requests&lt;br&gt;
are from CodeSandbox, because of the CodeSandbox embed that was on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9c84x3ekaxk5z8i5p5nq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9c84x3ekaxk5z8i5p5nq.png" alt="The enormous network payloads list" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I remembered that CodeSandbox released their &lt;a href="https://sandpack.codesandbox.io/" rel="noopener noreferrer"&gt;Sandpack&lt;/a&gt; component toolkit that you&lt;br&gt;
can use to create live code editing blocks. Since that's an npm package that you install, I figured it will definitely be&lt;br&gt;
more performant because its code will be compiled, optimized and shipped along with the page. So I decided to swap out&lt;br&gt;
the old embedded &lt;code&gt;iframe&lt;/code&gt; with the new Sandpack component. And the results were surprizing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7vhv6pcx5cz3y6t6yr9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7vhv6pcx5cz3y6t6yr9.png" alt="Improved PageSpeed Metrics" width="800" height="748"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of the metrics have been improved significantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLS: &lt;code&gt;0.029&lt;/code&gt; -&amp;gt; &lt;code&gt;0&lt;/code&gt; 🚀&lt;/li&gt;
&lt;li&gt;FCP: &lt;code&gt;0.6s&lt;/code&gt; -&amp;gt; &lt;code&gt;0.4s&lt;/code&gt; 🚀&lt;/li&gt;
&lt;li&gt;LCP: &lt;code&gt;0.6s&lt;/code&gt; -&amp;gt; &lt;code&gt;0.5s&lt;/code&gt; 🚀&lt;/li&gt;
&lt;li&gt;SI: &lt;code&gt;1.7s&lt;/code&gt; -&amp;gt; &lt;code&gt;0.6s&lt;/code&gt; 🚀🚀&lt;/li&gt;
&lt;li&gt;TTI: &lt;code&gt;9.8s&lt;/code&gt; -&amp;gt; &lt;code&gt;1.6s&lt;/code&gt; 🚀🚀🚀&lt;/li&gt;
&lt;li&gt;TBR: &lt;code&gt;4.9s&lt;/code&gt; -&amp;gt; &lt;code&gt;0.3s&lt;/code&gt; 🚀🚀🚀&lt;/li&gt;
&lt;li&gt;Overall Score: &lt;code&gt;58&lt;/code&gt; -&amp;gt; &lt;code&gt;86&lt;/code&gt; 🚀🚀🚀&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's walk through the changes. First, I installed the Sandpack package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add @codesandbox/sandpack-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I created a simple and generic component that displays the &lt;code&gt;Snowpack&lt;/code&gt; component, but allows the data to be provided&lt;br&gt;
from the outside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/components/sandpack-embed/index.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BoxProps&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;@chakra-ui/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Sandpack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SandpackProps&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;@codesandbox/sandpack-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@codesandbox/sandpack-react/dist/index.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SandpackEmbed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BoxProps&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;SandpackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Box&lt;/span&gt;
      &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Sandpack&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;showLineNumbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="nx"&gt;customSetup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;17.0.2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;17.0.2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-scripts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-icons&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3.11.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@chakra-ui/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.7.3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@chakra-ui/icons&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^1.1.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@emotion/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^11.7.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@emotion/styled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^11.6.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;framer-motion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^4.1.17&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="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;It's basically a &lt;code&gt;Box&lt;/code&gt; component, rendered as a &lt;code&gt;Snowpack&lt;/code&gt; component, but its props are the &lt;code&gt;BoxProps&lt;/code&gt; merged with &lt;code&gt;SandpackProps&lt;/code&gt;.&lt;br&gt;
That way we can pass Chakra style props, and Sandpack configuration props and reuse this component everywhere.&lt;/p&gt;

&lt;p&gt;Then I simply replaced the &lt;code&gt;iframe&lt;/code&gt; on the homepage with the new &lt;code&gt;SandpackEmbed&lt;/code&gt; component:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SandpackEmbed&lt;/span&gt;
  &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
    &lt;span class="na"&gt;editorHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;editorWidthPercentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/src/App.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/src/index.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}}&lt;/span&gt;
  &lt;span class="nx"&gt;zIndex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;tabIndex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;App&lt;/code&gt; and &lt;code&gt;Index&lt;/code&gt; variables that you see on line 7 and 8 are simple strings. It's the code content that we want to have&lt;br&gt;
inside of each file specifically:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`import * as React from "react";
import { render } from "react-dom";
import { ChakraProvider } from "@chakra-ui/react";
import App from "./App";
const rootElement = document.getElementById("root");
render(
  &amp;lt;ChakraProvider&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/ChakraProvider&amp;gt;,
  rootElement
);`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that was it! A simple change drastically improved the page speed, and also opened up possibilities to reuse the&lt;br&gt;
&lt;code&gt;SandpackEmbed&lt;/code&gt; component throughout the whole website. I plan on swapping the &lt;a href="https://www.npmjs.com/package/react-live" rel="noopener noreferrer"&gt;react-live&lt;/a&gt;&lt;br&gt;
package with the &lt;code&gt;SandpackEmbed&lt;/code&gt; as well. I'm not sure about performance boost, but a bonus feature will be the ability&lt;br&gt;
to create a CodeSandbox sandbox directly from the website.&lt;/p&gt;

&lt;p&gt;Here's the PR if you're interested to see all of the details about it:&lt;br&gt;
&lt;a href="https://github.com/chakra-ui/chakra-ui-docs/pull/154" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopengraph.githubassets.com%2Ff4a95bd3aa5113a1f599f5a810edeb16b885f3364b0443dc3c34a02c3290a5d8%2Fchakra-ui%2Fchakra-ui-docs%2Fpull%2F154" alt="Pull Request" width="1200" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>performance</category>
      <category>nextjs</category>
      <category>chakraui</category>
    </item>
    <item>
      <title>I've published the first official Chakra UI course</title>
      <dc:creator>Lazar Nikolov</dc:creator>
      <pubDate>Thu, 09 Sep 2021 13:23:51 +0000</pubDate>
      <link>https://dev.to/nikolovlazar/i-ve-published-the-first-official-chakra-ui-course-1p58</link>
      <guid>https://dev.to/nikolovlazar/i-ve-published-the-first-official-chakra-ui-course-1p58</guid>
      <description>&lt;p&gt;Hey everyone 👋&lt;/p&gt;

&lt;p&gt;I've published the first &lt;a href="https://egghead.io/courses/build-a-modern-user-interface-with-chakra-ui-fac68106?af=5zzhqq" rel="noopener noreferrer"&gt;official Chakra UI course&lt;/a&gt; on egghead! It's free and it'll get you up to speed with Chakra UI in 40 minutes.&lt;/p&gt;

&lt;p&gt;Check it out and let me know what you think.&lt;/p&gt;

&lt;p&gt;Thanks!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>react</category>
      <category>chakraui</category>
      <category>egghead</category>
    </item>
  </channel>
</rss>
